Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>0}");
static final DiagnosticType SHIFT_AMOUNT_OUT_OF_BOUNDS = DiagnosticType.error(
"JSC_SHIFT_AMOUNT_OUT_OF_BOUNDS",
"Shift amount out of bounds: {0}");
static final DiagnosticType FRACTIONAL_BITWISE_OPERAND = DiagnosticType.error(
"JSC_FRACTIONAL_BITWISE_OPERAND",
"Fractional bitwise operand: {0}");
private static final double MAX_FOLD_NUMBER = Math.pow(2, 53);
// The LOCALE independent "locale"
private static final Locale ROOT_LOCALE = new Locale("");
@Override
Node optimizeSubtree(Node subtree) {
switch(subtree.getType()) {
case Token.CALL:
return tryFoldKnownMethods(subtree);
case Token.NEW:
return tryFoldCtorCall(subtree);
case Token.TYPEOF:
return tryFoldTypeof(subtree);
case Token.NOT:
case Token.POS:
case Token.NEG:
case Token.BITNOT:
tryReduceOperandsForOp(subtree);
return tryFoldUnaryOperator(subtree);
case Token.VOID:
return tryReduceVoid(subtree);
default:
tryReduceOperandsForOp(subtree);
return tryFoldBinaryOperator(subtree);
}
}
private Node tryFoldBinaryOperator(Node subtree) {
Node left = subtree.getFirstChild();
if (left == null) {
return subtree;
}
Node right = left.getNext();
if (right == null) {
return subtree;
}
// If we've reached here, node is truly a binary operator.
switch(subtree.getType()) {
case Token.GETPROP:
return tryFoldGetProp(subtree, left, right);
case Token.GETELEM:
return tryFoldGetElem(subtree, left, right);
case Token.INSTANCEOF:
return tryFoldInstanceof(subtree, left, right);
case Token.AND:
case Token.OR:
return tryFoldAndOr(subtree, left, right);
case Token.LSH:
case Token.RSH:
case Token.URSH:
return tryFoldShift(subtree, left, right);
case Token.ASSIGN:
return tryFold
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>AND:
case Token.LSH:
case Token.RSH:
case Token.URSH:
case Token.SUB:
case Token.MUL:
case Token.MOD:
case Token.DIV:
case Token.POS:
case Token.NEG:
tryConvertOperandsToNumber(n);
break;
}
}
private void tryConvertOperandsToNumber(Node n) {
Node next;
for (Node c = n.getFirstChild(); c != null; c = next) {
next = c.getNext();
tryConvertToNumber(c);
}
}
private void tryConvertToNumber(Node n) {
switch (n.getType()) {
case Token.NUMBER:
// Nothing to do
return;
case Token.AND:
case Token.OR:
case Token.COMMA:
tryConvertToNumber(n.getLastChild());
return;
case Token.HOOK:
tryConvertToNumber(n.getChildAtIndex(1));
tryConvertToNumber(n.getLastChild());
return;
case Token.NAME:
if (!NodeUtil.isUndefined(n)) {
return;
}
break;
}
Double result = NodeUtil.getNumberValue(n);
if (result == null) {
return;
}
double value = result;
Node replacement;
if (Double.isNaN(value)) {
replacement = Node.newString(Token.NAME, "NaN");
} else if (value == Double.POSITIVE_INFINITY) {
replacement = Node.newString(Token.NAME, "Infinity");
} else if (value == Double.NEGATIVE_INFINITY) {
replacement = new Node(Token.NEG, Node.newString(Token.NAME, "Infinity"));
replacement.copyInformationFromForTree(n);
} else {
replacement = Node.newNumber(value);
}
n.getParent().replaceChild(n, replacement);
reportCodeChange();
}
/**
* Folds 'typeof(foo)' if foo is a literal, e.g.
* typeof("bar") --> "string"
* typeof(6) --> "number"
*/
private Node tryFoldTypeof(Node originalTypeofNode) {
Preconditions.checkArgument(originalTypeofNode.getType() == Token.TYPEOF);
Node argumentNode = originalTypeofNode.
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>getFirstChild();
if (argumentNode == null || !NodeUtil.isLiteralValue(argumentNode, true)) {
return originalTypeofNode;
}
String typeNameString = null;
switch (argumentNode.getType()) {
case Token.FUNCTION:
typeNameString = "function";
break;
case Token.STRING:
typeNameString = "string";
break;
case Token.NUMBER:
typeNameString = "number";
break;
case Token.TRUE:
case Token.FALSE:
typeNameString = "boolean";
break;
case Token.NULL:
case Token.OBJECTLIT:
case Token.ARRAYLIT:
typeNameString = "object";
break;
case Token.VOID:
typeNameString = "undefined";
break;
case Token.NAME:
// We assume here that programs don't change the value of the
// keyword undefined to something other than the value undefined.
if ("undefined".equals(argumentNode.getString())) {
typeNameString = "undefined";
}
break;
}
if (typeNameString != null) {
Node newNode = Node.newString(typeNameString);
originalTypeofNode.getParent().replaceChild(originalTypeofNode, newNode);
reportCodeChange();
return newNode;
}
return originalTypeofNode;
}
private Node tryFoldUnaryOperator(Node n) {
Preconditions.checkState(n.hasOneChild());
Node left = n.getFirstChild();
Node parent = n.getParent();
if (left == null) {
return n;
}
TernaryValue leftVal = NodeUtil.getPureBooleanValue(left);
if (leftVal == TernaryValue.UNKNOWN) {
return n;
}
switch (n.getType()) {
case Token.NOT:
// Don't fold !0 and !1 back to false.
if (left.getType() == Token.NUMBER) {
double numValue = left.getDouble();
if (numValue == 0 || numValue == 1) {
return n;
}
}
int result = leftVal.toBoolean(true) ? Token.FALSE : Token.TRUE;
Node replacementNode = new Node(result);
parent.replaceChild(n, replacementNode);
reportCodeChange();
return replacementNode;
case Token.POS:
if (NodeUtil.is
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>;
case Token.MOD:
newType = Token.ASSIGN_MOD;
break;
case Token.MUL:
newType = Token.ASSIGN_MUL;
break;
case Token.RSH:
newType = Token.ASSIGN_RSH;
break;
case Token.SUB:
newType = Token.ASSIGN_SUB;
break;
case Token.URSH:
newType = Token.ASSIGN_URSH;
break;
default:
return n;
}
Node newNode = new Node(newType,
left.detachFromParent(), newRight.detachFromParent());
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* Try to fold a AND/OR node.
*/
private Node tryFoldAndOr(Node n, Node left, Node right) {
Node parent = n.getParent();
Node result = null;
int type = n.getType();
TernaryValue leftVal = NodeUtil.getImpureBooleanValue(left);
if (leftVal != TernaryValue.UNKNOWN) {
boolean lval = leftVal.toBoolean(true);
// (TRUE || x) => TRUE (also, (3 || x) => 3)
// (FALSE && x) => FALSE
if (lval && type == Token.OR ||
!lval && type == Token.AND) {
result = left;
} else if (!mayHaveSideEffects(left)) {
// (FALSE || x) => x
// (TRUE && x) => x
result = right;
}
}
// Note: Right hand side folding is handled by
// PeepholeSubstituteAlternateSyntax#tryMinimizeCondition
if (result != null) {
// Fold it!
n.removeChild(result);
parent.replaceChild(n, result);
reportCodeChange();
return result;
} else {
return n;
}
}
/**
* Expressions such as [foo() + 'a' + 'b'] generate parse trees
* where no node has two const children ((foo() + 'a') + 'b'), so
* tryFoldAdd() won't fold it -- tryFoldLeftChildAdd() will (for Strings).
* Specifically it folds Add exprssions where:
*
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> value The value to compare to "undefined"
* @param op The boolean op to compare with
* @return Whether the boolean op is true or false
*/
private boolean compareToUndefined(Node value, int op) {
boolean valueUndefined = ((Token.NAME == value.getType()
&& value.getString().equals("undefined"))
|| (Token.VOID == value.getType()
&& NodeUtil.isLiteralValue(value.getFirstChild(), false)));
boolean valueNull = (Token.NULL == value.getType());
boolean equivalent = valueUndefined || valueNull;
switch (op) {
case Token.EQ:
// undefined is only equal to null or an undefined value
return equivalent;
case Token.NE:
return !equivalent;
case Token.SHEQ:
return valueUndefined;
case Token.SHNE:
return !valueUndefined;
case Token.LT:
case Token.GT:
case Token.LE:
case Token.GE:
return false;
default:
throw new IllegalStateException("unexpected.");
}
}
/**
* Try to fold away unnecessary object instantiation.
* e.g. this[new String('eval')] -> this.eval
*/
private Node tryFoldCtorCall(Node n) {
Preconditions.checkArgument(n.getType() == Token.NEW);
// we can remove this for GETELEM calls (anywhere else?)
if (inForcedStringContext(n)) {
return tryFoldInForcedStringContext(n);
}
return n;
}
/** Returns whether this node must be coerced to a string. */
private boolean inForcedStringContext(Node n) {
return n.getParent().getType() == Token.GETELEM &&
n.getParent().getLastChild() == n;
}
private Node tryFoldInForcedStringContext(Node n) {
// For now, we only know how to fold ctors.
Preconditions.checkArgument(n.getType() == Token.NEW);
Node objectType = n.getFirstChild();
if (objectType.getType() != Token.NAME) {
return n;
}
if (objectType.getString().equals("String")) {
Node value = objectType.getNext();
String stringValue = null;
if (value == null) {
stringValue = "";
} else {
if
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> (!NodeUtil.isImmutableValue(value)) {
return n;
}
stringValue = NodeUtil.getStringValue(value);
}
if (stringValue == null) {
return n;
}
Node parent = n.getParent();
Node newString = Node.newString(stringValue);
parent.replaceChild(n, newString);
newString.copyInformationFrom(parent);
reportCodeChange();
return newString;
}
return n;
}
private Node tryFoldKnownMethods(Node subtree) {
// For now we only support .join(),
// .indexOf(), .substring() and .substr()
subtree = tryFoldArrayJoin(subtree);
if (subtree.getType() == Token.CALL) {
subtree = tryFoldKnownStringMethods(subtree);
}
return subtree;
}
/**
* Try to eveluate known String methods
* .indexOf(), .substr(), .substring()
*/
private Node tryFoldKnownStringMethods(Node subtree) {
Preconditions.checkArgument(subtree.getType() == Token.CALL);
// check if this is a call on a string method
// then dispatch to specific folding method.
Node callTarget = subtree.getFirstChild();
if (callTarget == null) {
return subtree;
}
if (!NodeUtil.isGet(callTarget)) {
return subtree;
}
Node stringNode = callTarget.getFirstChild();
Node functionName = stringNode.getNext();
if ((stringNode.getType() != Token.STRING) || (
(functionName.getType() != Token.STRING))) {
return subtree;
}
String functionNameString = functionName.getString();
Node firstArg = callTarget.getNext();
if (firstArg == null) {
if (functionNameString.equals("toLowerCase")) {
subtree = tryFoldStringToLowerCase(subtree, stringNode);
} else if (functionNameString.equals("toUpperCase")) {
subtree = tryFoldStringToUpperCase(subtree, stringNode);
}
return subtree;
} else if (NodeUtil.isImmutableValue(firstArg)) {
if (functionNameString.equals("indexOf") ||
functionNameString.equals("lastIndexOf")) {
subtree = tryFoldStringIndexOf(subtree, functionNameString,
stringNode, firstArg);
} else if
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> here:
// "{a:x}.a += 1" is not "x += 1"
return n;
}
// find the last definition in the object literal
Node key = null;
Node value = null;
for (Node c = left.getFirstChild(); c != null; c = c.getNext()) {
if (c.getString().equals(right.getString())) {
switch (c.getType()) {
case Token.SET:
continue;
case Token.GET:
case Token.STRING:
if (value != null && mayHaveSideEffects(value)) {
// The previously found value had side-effects
return n;
}
key = c;
value = key.getFirstChild();
break;
default:
throw new IllegalStateException();
}
} else if (mayHaveSideEffects(c.getFirstChild())) {
// We don't handle the side-effects here as they might need a temporary
// or need to be reordered.
return n;
}
}
// Didn't find a definition of the name in the object literal, it might
// be coming from the Object prototype
if (value == null) {
return n;
}
if (value.getType() == Token.FUNCTION && NodeUtil.referencesThis(value)) {
// 'this' may refer to the object we are trying to remove
return n;
}
Node replacement = value.detachFromParent();
if (key.getType() == Token.GET){
replacement = new Node(Token.CALL, replacement);
}
n.getParent().replaceChild(n, replacement);
reportCodeChange();
return n;
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
void endCaseBody() {
super.endCaseBody();
indent--;
endStatement();
}
@Override
void appendOp(String op, boolean binOp) {
if (binOp) {
if (getLastChar() != ' ') {
append(" ");
}
append(op);
append(" ");
} else {
append(op);
}
}
/**
* If the body of a for loop or the then clause of an if statement has
* a single statement, should it be wrapped in a block?
* {@inheritDoc}
*/
@Override
boolean shouldPreserveExtraBlocks() {
// When pretty-printing, always place the statement in its own block
// so it is printed on a separate line. This allows breakpoints to be
// placed on the statement.
return true;
}
/**
* @return The TRY node for the specified CATCH node.
*/
private Node getTryForCatch(Node n) {
return n.getParent().getParent();
}
/**
* @return Whether the a line break should be added after the specified
* BLOCK.
*/
@Override
boolean breakAfterBlockFor(Node n, boolean isStatementContext) {
Preconditions.checkState(n.getType() == Token.BLOCK);
Node parent = n.getParent();
if (parent != null) {
int type = parent.getType();
switch (type) {
case Token.DO:
// Don't break before 'while' in DO-WHILE statements.
return false;
case Token.FUNCTION:
// FUNCTIONs are handled separately, don't break here.
return false;
case Token.TRY:
// Don't break before catch
return n != parent.getFirstChild();
case Token.CATCH:
// Don't break before finally
return !NodeUtil.hasFinally(getTryForCatch(parent));
case Token.IF:
// Don't break before else
return n == parent.getLastChild();
}
}
return true;
}
@Override
void endFile() {
maybeEndStatement();
}
}
static class CompactCodePrinter
extends MappedCodePrinter {
// The CompactCodePrinter tries to emit just enough newlines to stop there
// being lines longer than the threshold. Since the output is going to be
// gzipped,
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
return caseEquality(condition, blindScope, EQ);
}
case Token.SHEQ:
if (outcome) {
return caseEquality(condition, blindScope, SHEQ);
} else {
return caseEquality(condition, blindScope, SHNE);
}
case Token.SHNE:
if (outcome) {
return caseEquality(condition, blindScope, SHNE);
} else {
return caseEquality(condition, blindScope, SHEQ);
}
case Token.NAME:
case Token.GETPROP:
return caseNameOrGetProp(condition, blindScope, outcome);
case Token.ASSIGN:
return firstPreciserScopeKnowingConditionOutcome(
condition.getFirstChild(),
firstPreciserScopeKnowingConditionOutcome(
condition.getFirstChild().getNext(), blindScope, outcome),
outcome);
case Token.NOT:
return firstPreciserScopeKnowingConditionOutcome(
condition.getFirstChild(), blindScope, !outcome);
case Token.LE:
case Token.LT:
case Token.GE:
case Token.GT:
if (outcome) {
return caseEquality(condition, blindScope, INEQ);
}
break;
case Token.INSTANCEOF:
return caseInstanceOf(
condition.getFirstChild(), condition.getLastChild(), blindScope,
outcome);
case Token.IN:
if (outcome && condition.getFirstChild().getType() == Token.STRING) {
return caseIn(condition.getLastChild(),
condition.getFirstChild().getString(), blindScope);
}
break;
case Token.CASE:
Node left =
condition.getParent().getFirstChild(); // the switch condition
Node right = condition.getFirstChild();
if (outcome) {
return caseEquality(left, right, blindScope, SHEQ);
} else {
return caseEquality(left, right, blindScope, SHNE);
}
}
return nextPreciserScopeKnowingConditionOutcome(
condition, blindScope, outcome);
}
private FlowScope caseEquality(Node condition, FlowScope blindScope,
Function<TypePair, TypePair> merging) {
return caseEquality(condition.getFirstChild(), condition.getLastChild(),
blindScope, merging);
}
private FlowScope caseEquality(Node left, Node right, FlowScope blindScope,
Function<Type
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> getTypeIfRefinable(right, blindScope);
boolean rightIsRefineable;
if (rightType != null) {
rightIsRefineable = true;
} else {
rightIsRefineable = false;
rightType = right.getJSType();
blindScope = firstPreciserScopeKnowingConditionOutcome(
right, blindScope, condition);
}
if (condition) {
rightType = (rightType == null) ? null :
rightType.getRestrictedTypeGivenToBooleanOutcome(condition);
// creating new scope
if ((leftType != null && leftIsRefineable) ||
(rightType != null && rightIsRefineable)) {
FlowScope informed = blindScope.createChildFlowScope();
if (leftIsRefineable && leftType != null) {
declareNameInScope(informed, left, leftType);
}
if (rightIsRefineable && rightType != null) {
declareNameInScope(informed, right, rightType);
}
return informed;
}
}
return blindScope;
}
private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right,
FlowScope blindScope, boolean condition) {
FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome(
left, blindScope, !condition);
StaticSlot<JSType> leftVar = leftScope.findUniqueRefinedSlot(blindScope);
if (leftVar == null) {
return blindScope;
}
FlowScope rightScope = firstPreciserScopeKnowingConditionOutcome(
left, blindScope, condition);
rightScope = firstPreciserScopeKnowingConditionOutcome(
right, rightScope, !condition);
StaticSlot<JSType> rightVar = rightScope.findUniqueRefinedSlot(blindScope);
if (rightVar == null || !leftVar.getName().equals(rightVar.getName())) {
return blindScope;
}
JSType type = leftVar.getType().getLeastSupertype(rightVar.getType());
FlowScope informed = blindScope.createChildFlowScope();
informed.inferSlotType(leftVar.getName(), type);
return informed;
}
private FlowScope caseNameOrGetProp(Node name, FlowScope blindScope,
boolean outcome) {
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
JSType type = getTypeIfRefinable(name, blindScope);
if (type != null) {
JSType restrictedType =
type.getRestrictedTypeGivenToBooleanOutcome(outcome);
FlowScope informed = blindScope.createChildFlowScope();
declareNameInScope(informed, name, restrictedType);
return informed;
}
return blindScope;
}
private FlowScope caseTypeOf(Node node, JSType type, String value,
boolean resultEqualsValue, FlowScope blindScope) {
JSType restrictedType =
getRestrictedByTypeOfResult(type, value, resultEqualsValue);
if (restrictedType == null) {
return blindScope;
}
FlowScope informed = blindScope.createChildFlowScope();
declareNameInScope(informed, node, restrictedType);
return informed;
}
private FlowScope caseInstanceOf(Node left, Node right, FlowScope blindScope,
boolean outcome) {
JSType leftType = getTypeIfRefinable(left, blindScope);
if (leftType == null) {
return blindScope;
}
JSType rightType = right.getJSType();
ObjectType targetType =
typeRegistry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE);
if (rightType instanceof FunctionType) {
targetType = (FunctionType) rightType;
}
Visitor<JSType> visitor;
if (outcome) {
visitor = new RestrictByTrueInstanceOfResultVisitor(targetType);
} else {
visitor = new RestrictByFalseInstanceOfResultVisitor(targetType);
}
JSType restrictedLeftType = leftType.visit(visitor);
if (restrictedLeftType != null && !restrictedLeftType.equals(leftType)) {
FlowScope informed = blindScope.createChildFlowScope();
declareNameInScope(informed, left, restrictedLeftType);
return informed;
}
return blindScope;
}
/**
* Given 'property in object', ensures that the object has the property in the
* informed scope by defining it as a qualified name if the object type lacks
* the property and it's not in the blind scope.
* @param object The node of the right-side of the in.
* @param propertyName The string of the left-side of the in.
*/
private FlowScope case
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>In(Node object, String propertyName, FlowScope blindScope) {
JSType jsType = object.getJSType();
jsType = this.getRestrictedWithoutNull(jsType);
jsType = this.getRestrictedWithoutUndefined(jsType);
boolean hasProperty = false;
ObjectType objectType = ObjectType.cast(jsType);
if (objectType != null) {
hasProperty = objectType.hasProperty(propertyName);
}
if (!hasProperty) {
String qualifiedName = object.getQualifiedName();
if (qualifiedName != null) {
String propertyQualifiedName = qualifiedName + "." + propertyName;
if (blindScope.getSlot(propertyQualifiedName) == null) {
FlowScope informed = blindScope.createChildFlowScope();
JSType unknownType = typeRegistry.getNativeType(
JSTypeNative.UNKNOWN_TYPE);
informed.inferQualifiedSlot(
propertyQualifiedName, unknownType, unknownType);
return informed;
}
}
}
return blindScope;
}
/**
* @see SemanticReverseAbstractInterpreter#caseInstanceOf
*/
private class RestrictByTrueInstanceOfResultVisitor
extends RestrictByTrueTypeOfResultVisitor {
private final ObjectType target;
RestrictByTrueInstanceOfResultVisitor(ObjectType target) {
this.target = target;
}
@Override
protected JSType caseTopType(JSType type) {
return applyCommonRestriction(type);
}
@Override
public JSType caseUnknownType() {
if (target instanceof FunctionType) {
FunctionType funcTarget = (FunctionType) target;
if (funcTarget.hasInstanceType()) {
return funcTarget.getInstanceType();
}
}
return getNativeType(UNKNOWN_TYPE);
}
@Override
public JSType caseObjectType(ObjectType type) {
return applyCommonRestriction(type);
}
@Override
public JSType caseUnionType(UnionType type) {
return applyCommonRestriction(type);
}
@Override
public JSType caseFunctionType(FunctionType type) {
return caseObjectType(type);
}
private JSType applyCommonRestriction(JSType type) {
if (target.isUnknownType()) {
return type;
}
FunctionType funcTarget = (FunctionType) target;
if (funcTarget.hasInstanceType()) {
return type.getGreatestSubtype(func
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>Target.getInstanceType());
}
return null;
}
}
/**
* @see SemanticReverseAbstractInterpreter#caseInstanceOf
*/
private class RestrictByFalseInstanceOfResultVisitor
extends RestrictByFalseTypeOfResultVisitor {
private final ObjectType target;
RestrictByFalseInstanceOfResultVisitor(ObjectType target) {
this.target = target;
}
@Override
public JSType caseObjectType(ObjectType type) {
if (target.isUnknownType()) {
return type;
}
FunctionType funcTarget = (FunctionType) target;
if (funcTarget.hasInstanceType()) {
if (type.isSubtype(funcTarget.getInstanceType())) {
return null;
}
return type;
}
return null;
}
@Override
public JSType caseUnionType(UnionType type) {
if (target.isUnknownType()) {
return type;
}
FunctionType funcTarget = (FunctionType) target;
if (funcTarget.hasInstanceType()) {
return type.getRestrictedUnion(funcTarget.getInstanceType());
}
return null;
}
@Override
public JSType caseFunctionType(FunctionType type) {
return caseObjectType(type);
}
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>(n2);
for (DiGraphEdge<N, E> outEdge : dNode1.getOutEdges()) {
if (outEdge.getDestination() == dNode2) {
return outEdge;
}
}
for (DiGraphEdge<N, E> outEdge : dNode2.getOutEdges()) {
if (outEdge.getDestination() == dNode1) {
return outEdge;
}
}
return null;
}
@Override
public GraphNode<N, E> createNode(N value) {
return createDirectedGraphNode(value);
}
@Override
public List<DiGraphEdge<N, E>> getDirectedGraphEdges(N n1, N n2) {
DiGraphNode<N, E> dNode1 = getNodeOrFail(n1);
DiGraphNode<N, E> dNode2 = getNodeOrFail(n2);
List<DiGraphEdge<N, E>> edges = Lists.newArrayList();
for (DiGraphEdge<N, E> outEdge : dNode1.getOutEdges()) {
if (outEdge.getDestination() == dNode2) {
edges.add(outEdge);
}
}
return edges;
}
@Override
public boolean isConnectedInDirection(N n1, N n2) {
return isConnectedInDirection(n1, Predicates.<E>alwaysTrue(), n2);
}
@Override
public boolean isConnectedInDirection(N n1, E edgeValue, N n2) {
return isConnectedInDirection(n1, Predicates.equalTo(edgeValue), n2);
}
private boolean isConnectedInDirection(N n1, Predicate<E> edgeMatcher, N n2) {
// Verify the nodes.
DiGraphNode<N, E> dNode1 = getNodeOrFail(n1);
DiGraphNode<N, E> dNode2 = getNodeOrFail(n2);
for (DiGraphEdge<N, E> outEdge : dNode1.getOutEdges()) {
if (outEdge.getDestination() == dNode2 &&
edgeMatcher.apply(outEdge.getValue())) {
return true;
}
}
return false;
}
@Override
public List<DiGraphNode<N, E>> get
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> @Override
public Collection<GraphNode<N, E>> getNodes() {
return Collections.<GraphNode<N, E>>unmodifiableCollection(nodes.values());
}
@Override
public List<GraphNode<N, E>> getNeighborNodes(N value) {
DiGraphNode<N, E> node = getDirectedGraphNode(value);
return getNeighborNodes(node);
}
public List<GraphNode<N, E>> getNeighborNodes(DiGraphNode<N, E> node) {
List<GraphNode<N, E>> result = Lists.newArrayList();
for (Iterator<GraphNode<N, E>> i =
((LinkedDirectedGraphNode<N, E>) node).neighborIterator();i.hasNext();) {
result.add(i.next());
}
return result;
}
@Override
public Iterator<GraphNode<N, E>> getNeighborNodesIterator(N value) {
LinkedDirectedGraphNode<N, E> node = nodes.get(value);
Preconditions.checkNotNull(node);
return node.neighborIterator();
}
@Override
public List<GraphEdge<N, E>> getEdges() {
List<GraphEdge<N, E>> result = Lists.newArrayList();
for (DiGraphNode<N, E> node : nodes.values()) {
for (DiGraphEdge<N, E> edge : node.getOutEdges()) {
result.add(edge);
}
}
return Collections.unmodifiableList(result);
}
@Override
public int getNodeDegree(N value) {
DiGraphNode<N, E> node = getNodeOrFail(value);
return node.getInEdges().size() + node.getOutEdges().size();
}
/**
* A directed graph node that stores outgoing edges and incoming edges as an
* list within the node itself.
*/
static class LinkedDirectedGraphNode<N, E> implements DiGraphNode<N, E>,
GraphvizNode {
List<DiGraphEdge<N, E>> inEdgeList = Lists.newArrayList();
List<DiGraphEdge<N, E>> outEdgeList =
Lists.newArrayList();
protected final N value;
/**
* Constructor
*
* @param nodeValue Node's value.
*/
LinkedDirectedGraphNode(N nodeValue
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>) {
this.value = nodeValue;
}
@Override
public N getValue() {
return value;
}
@Override
public <A extends Annotation> A getAnnotation() {
throw new UnsupportedOperationException(
"Graph initialized with node annotations turned off");
}
@Override
public void setAnnotation(Annotation data) {
throw new UnsupportedOperationException(
"Graph initialized with node annotations turned off");
}
@Override
public String getColor() {
return "white";
}
@Override
public String getId() {
return "LDN" + hashCode();
}
@Override
public String getLabel() {
return value != null ? value.toString() : "null";
}
@Override
public String toString() {
return getLabel();
}
@Override
public List<DiGraphEdge<N, E>> getInEdges() {
return inEdgeList;
}
@Override
public List<DiGraphEdge<N, E>> getOutEdges() {
return outEdgeList;
}
private Iterator<GraphNode<N, E>> neighborIterator() {
return new NeighborIterator();
}
private class NeighborIterator implements Iterator<GraphNode<N, E>> {
private final Iterator<DiGraphEdge<N, E>> in = inEdgeList.iterator();
private final Iterator<DiGraphEdge<N, E>> out = outEdgeList.iterator();
@Override
public boolean hasNext() {
return in.hasNext() || out.hasNext();
}
@Override
public GraphNode<N, E> next() {
boolean isOut = !in.hasNext();
Iterator<DiGraphEdge<N, E>> curIterator = isOut ? out : in;
DiGraphEdge<N, E> s = curIterator.next();
return isOut ? s.getDestination() : s.getSource();
}
@Override
public void remove() {
throw new UnsupportedOperationException("Remove not supported.");
}
}
}
/**
* A directed graph node with annotations.
*/
static class AnnotatedLinkedDirectedGraphNode<N, E>
extends LinkedDirectedGraphNode<N, E> {
protected Annotation annotation;
/**
* @param nodeValue Node's value.
*/
AnnotatedLinkedDirectedGraphNode(N nodeValue) {
super(nodeValue
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>);
}
@SuppressWarnings("unchecked")
@Override
public <A extends Annotation> A getAnnotation() {
return (A) annotation;
}
@Override
public void setAnnotation(Annotation data) {
annotation = data;
}
}
/**
* A directed graph edge that stores the source and destination nodes at each
* edge.
*/
static class LinkedDirectedGraphEdge<N, E> implements DiGraphEdge<N, E>,
GraphvizEdge {
private DiGraphNode<N, E> sourceNode;
private DiGraphNode<N, E> destNode;
protected final E value;
/**
* Constructor.
*
* @param edgeValue Edge Value.
*/
LinkedDirectedGraphEdge(DiGraphNode<N, E> sourceNode,
E edgeValue, DiGraphNode<N, E> destNode) {
this.value = edgeValue;
this.sourceNode = sourceNode;
this.destNode = destNode;
}
@Override
public DiGraphNode<N, E> getSource() {
return sourceNode;
}
@Override
public DiGraphNode<N, E> getDestination() {
return destNode;
}
@Override
public void setDestination(DiGraphNode<N, E> node) {
destNode = node;
}
@Override
public void setSource(DiGraphNode<N, E> node) {
sourceNode = node;
}
@Override
public E getValue() {
return value;
}
@Override
public <A extends Annotation> A getAnnotation() {
throw new UnsupportedOperationException(
"Graph initialized with edge annotations turned off");
}
@Override
public void setAnnotation(Annotation data) {
throw new UnsupportedOperationException(
"Graph initialized with edge annotations turned off");
}
@Override
public String getColor() {
return "black";
}
@Override
public String getLabel() {
return value != null ? value.toString() : "null";
}
@Override
public String getNode1Id() {
return ((LinkedDirectedGraphNode<N, E>) sourceNode).getId();
}
@Override
public String getNode2Id() {
return ((LinkedDirectedGraphNode<N, E>) destNode).getId();
}
@Override
public String toString()
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> private final CodingConvention convention;
PrepareAnnotations(AbstractCompiler compiler) {
this.convention = compiler.getCodingConvention();
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.OBJECTLIT) {
normalizeObjectLiteralAnnotations(n);
}
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.CALL:
annotateCalls(n);
break;
case Token.FUNCTION:
annotateFunctions(n, parent);
annotateDispatchers(n, parent);
break;
}
}
private void normalizeObjectLiteralAnnotations(Node objlit) {
Preconditions.checkState(objlit.getType() == Token.OBJECTLIT);
for (Node key = objlit.getFirstChild();
key != null; key = key.getNext()) {
Node value = key.getFirstChild();
normalizeObjectLiteralKeyAnnotations(objlit, key, value);
}
}
/**
* There are two types of calls we are interested in calls without explicit
* "this" values (what we are call "free" calls) and direct call to eval.
*/
private void annotateCalls(Node n) {
Preconditions.checkState(n.getType() == Token.CALL);
// Keep track of of the "this" context of a call. A call without an
// explicit "this" is a free call.
Node first = n.getFirstChild();
if (!NodeUtil.isGet(first)) {
n.putBooleanProp(Node.FREE_CALL, true);
}
// Keep track of the context in which eval is called. It is important
// to distinguish between "(0, eval)()" and "eval()".
if (first.getType() == Token.NAME &&
"eval".equals(first.getString())) {
first.putBooleanProp(Node.DIRECT_EVAL, true);
}
}
/**
* Translate dispatcher info into the property expected node.
*/
private void annotateDispatchers(Node n, Node parent) {
Preconditions.checkState(n.getType() == Token.FUNCTION);
if (parent.getJSDocInfo() != null
&& parent.getJSDocInfo().isJavaDispatch())
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> {
if (parent.getType() == Token.ASSIGN) {
Preconditions.checkState(parent.getLastChild() == n);
n.putBooleanProp(Node.IS_DISPATCHER, true);
}
}
}
/**
* In the AST that Rhino gives us, it needs to make a distinction
* between jsdoc on the object literal node and jsdoc on the object literal
* value. For example,
* <pre>
* var x = {
* / JSDOC /
* a: 'b',
* c: / JSDOC / 'd'
* };
* </pre>
*
* But in few narrow cases (in particular, function literals), it's
* a lot easier for us if the doc is attached to the value.
*/
private void normalizeObjectLiteralKeyAnnotations(
Node objlit, Node key, Node value) {
Preconditions.checkState(objlit.getType() == Token.OBJECTLIT);
if (key.getJSDocInfo() != null &&
value.getType() == Token.FUNCTION) {
value.setJSDocInfo(key.getJSDocInfo());
}
}
/**
* Annotate optional and var_arg function parameters.
*/
private void annotateFunctions(Node n, Node parent) {
JSDocInfo fnInfo = NodeUtil.getFunctionInfo(n);
// Compute which function parameters are optional and
// which are var_args.
Node args = n.getFirstChild().getNext();
for (Node arg = args.getFirstChild();
arg != null;
arg = arg.getNext()) {
String argName = arg.getString();
JSTypeExpression typeExpr = fnInfo == null ?
null : fnInfo.getParameterType(argName);
if (convention.isOptionalParameter(arg) ||
typeExpr != null && typeExpr.isOptionalArg()) {
arg.putBooleanProp(Node.IS_OPTIONAL_PARAM, true);
}
if (convention.isVarArgsParameter(arg) ||
typeExpr != null && typeExpr.isVarArgs()) {
arg.putBooleanProp(Node.IS_VAR_ARGS_PARAM, true);
}
}
}
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>(NodeTraversal t, Node n, ...);
// If there is a mismatch, the {@code expect} method should issue
// a warning and attempt to correct the mismatch, when possible.
/**
* Expect the type to be an object, or a type convertible to object. If the
* expectation is not met, issue a warning at the provided node's source code
* position.
* @return True if there was no warning, false if there was a mismatch.
*/
boolean expectObject(NodeTraversal t, Node n, JSType type, String msg) {
if (!type.matchesObjectContext()) {
mismatch(t, n, msg, type, OBJECT_TYPE);
return false;
}
return true;
}
/**
* Expect the type to be an object. Unlike expectObject, a type convertible
* to object is not acceptable.
*/
void expectActualObject(NodeTraversal t, Node n, JSType type, String msg) {
if (!type.isObject()) {
mismatch(t, n, msg, type, OBJECT_TYPE);
}
}
/**
* Expect the type to contain an object sometimes. If the expectation is
* not met, issue a warning at the provided node's source code position.
*/
void expectAnyObject(NodeTraversal t, Node n, JSType type, String msg) {
JSType anyObjectType = getNativeType(NO_OBJECT_TYPE);
if (!anyObjectType.isSubtype(type) && !type.isEmptyType()) {
mismatch(t, n, msg, type, anyObjectType);
}
}
/**
* Expect the type to be a string, or a type convertible to string. If the
* expectation is not met, issue a warning at the provided node's source code
* position.
*/
void expectString(NodeTraversal t, Node n, JSType type, String msg) {
if (!type.matchesStringContext()) {
mismatch(t, n, msg, type, STRING_TYPE);
}
}
/**
* Expect the type to be a number, or a type convertible to number. If the
* expectation is not met, issue a warning at the provided node's source code
* position.
*/
void expectNumber(NodeTraversal t, Node n, JSType type, String msg) {
if (!type.
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>matchesNumberContext()) {
mismatch(t, n, msg, type, NUMBER_TYPE);
}
}
/**
* Expect the type to be a valid operand to a bitwise operator. This includes
* numbers, any type convertible to a number, or any other primitive type
* (undefined|null|boolean|string).
*/
void expectBitwiseable(NodeTraversal t, Node n, JSType type, String msg) {
if (!type.matchesNumberContext() && !type.isSubtype(allValueTypes)) {
mismatch(t, n, msg, type, allValueTypes);
}
}
/**
* Expect the type to be a number or string, or a type convertible to a number
* or string. If the expectation is not met, issue a warning at the provided
* node's source code position.
*/
void expectStringOrNumber(
NodeTraversal t, Node n, JSType type, String msg) {
if (!type.matchesNumberContext() && !type.matchesStringContext()) {
mismatch(t, n, msg, type, NUMBER_STRING);
}
}
/**
* Expect the type to be anything but the null or void type. If the
* expectation is not met, issue a warning at the provided node's
* source code position. Note that a union type that includes the
* void type and at least one other type meets the expectation.
* @return Whether the expectation was met.
*/
boolean expectNotNullOrUndefined(
NodeTraversal t, Node n, JSType type, String msg, JSType expectedType) {
if (!type.isNoType() && !type.isUnknownType() &&
type.isSubtype(nullOrUndefined) &&
!containsForwardDeclaredUnresolvedName(type)) {
// There's one edge case right now that we don't handle well, and
// that we don't want to warn about.
// if (this.x == null) {
// this.initializeX();
// this.x.foo();
// }
// In this case, we incorrectly type x because of how we
// infer properties locally. See issue 109.
// http://code.google.com/p/closure-compiler/issues/detail?id=109
//
// We do not do this inference globally.
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
if (n.getType() == Token.GETPROP &&
!t.inGlobalScope() && type.isNullType()) {
return true;
}
mismatch(t, n, msg, type, expectedType);
return false;
}
return true;
}
private boolean containsForwardDeclaredUnresolvedName(JSType type) {
if (type instanceof UnionType) {
for (JSType alt : ((UnionType) type).getAlternates()) {
if (containsForwardDeclaredUnresolvedName(alt)) {
return true;
}
}
}
return type.isNoResolvedType();
}
/**
* Expect that the type of a switch condition matches the type of its
* case condition.
*/
void expectSwitchMatchesCase(NodeTraversal t, Node n, JSType switchType,
JSType caseType) {
// ECMA-262, page 68, step 3 of evaluation of CaseBlock,
// but allowing extra autoboxing.
// TODO(user): remove extra conditions when type annotations
// in the code base have adapted to the change in the compiler.
if (!switchType.canTestForShallowEqualityWith(caseType) &&
(caseType.autoboxesTo() == null ||
!caseType.autoboxesTo().isSubtype(switchType))) {
mismatch(t, n.getFirstChild(),
"case expression doesn't match switch",
caseType, switchType);
}
}
/**
* Expect that the first type can be addressed with GETELEM syntax,
* and that the second type is the right type for an index into the
* first type.
*
* @param t The node traversal.
* @param n The node to issue warnings on.
* @param objType The type of the left side of the GETELEM.
* @param indexType The type inside the brackets of the GETELEM.
*/
void expectIndexMatch(NodeTraversal t, Node n, JSType objType,
JSType indexType) {
if (objType.isUnknownType()) {
expectStringOrNumber(t, n, indexType, "property access");
} else if (objType.toObjectType() != null &&
objType.toObjectType().getIndexType() != null) {
expectCanAssignTo(t, n, indexType, objType
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> expectCanOverride(NodeTraversal t, Node n, JSType overridingType,
JSType hiddenType, String propertyName, JSType ownerType) {
if (!overridingType.canAssignTo(hiddenType)) {
registerMismatch(overridingType, hiddenType);
if (shouldReport) {
compiler.report(
t.makeError(n, HIDDEN_PROPERTY_MISMATCH,
propertyName, ownerType.toString(),
hiddenType.toString(), overridingType.toString()));
}
}
}
/**
* Expect that the first type is the direct superclass of the second type.
*
* @param t The node traversal.
* @param n The node where warnings should point to.
* @param superObject The expected super instance type.
* @param subObject The sub instance type.
*/
void expectSuperType(NodeTraversal t, Node n, ObjectType superObject,
ObjectType subObject) {
FunctionType subCtor = subObject.getConstructor();
ObjectType declaredSuper =
subObject.getImplicitPrototype().getImplicitPrototype();
if (!declaredSuper.equals(superObject)) {
if (declaredSuper.equals(getNativeType(OBJECT_TYPE))) {
if (shouldReport) {
compiler.report(
t.makeError(n, MISSING_EXTENDS_TAG_WARNING,
subObject.toString()));
}
registerMismatch(superObject, declaredSuper);
} else {
mismatch(t.getSourceName(), n,
"mismatch in declaration of superclass type",
superObject, declaredSuper);
}
// Correct the super type.
if (!subCtor.hasCachedValues()) {
subCtor.setPrototypeBasedOn(superObject);
}
}
}
/**
* Expect that the first type can be cast to the second type. The first type
* should be either a subtype or supertype of the second.
*
* @param t The node traversal.
* @param n The node where warnings should point.
* @param type The type being cast from.
* @param castType The type being cast to.
*/
void expectCanCast(NodeTraversal t, Node n, JSType type, JSType castType) {
castType = castType.restrictByNotNullOrUndefined();
type = type.restrictByNotNullOrUndefined();
if (!type.canAssignTo(
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>castType) && !castType.canAssignTo(type)) {
if (shouldReport) {
compiler.report(
t.makeError(n, INVALID_CAST,
castType.toString(), type.toString()));
}
registerMismatch(type, castType);
}
}
/**
* Expect that the given variable has not been declared with a type.
*
* @param sourceName The name of the source file we're in.
* @param n The node where warnings should point to.
* @param parent The parent of {@code n}.
* @param var The variable that we're checking.
* @param variableName The name of the variable.
* @param newType The type being applied to the variable. Mostly just here
* for the benefit of the warning.
*/
void expectUndeclaredVariable(String sourceName, Node n, Node parent, Var var,
String variableName, JSType newType) {
boolean allowDupe = false;
if (n.getType() == Token.GETPROP ||
NodeUtil.isObjectLitKey(n, parent)) {
JSDocInfo info = n.getJSDocInfo();
if (info == null) {
info = parent.getJSDocInfo();
}
allowDupe =
info != null && info.getSuppressions().contains("duplicate");
}
JSType varType = var.getType();
// Only report duplicate declarations that have types. Other duplicates
// will be reported by the syntactic scope creator later in the
// compilation process.
if (varType != null &&
varType != typeRegistry.getNativeType(UNKNOWN_TYPE) &&
newType != null &&
newType != typeRegistry.getNativeType(UNKNOWN_TYPE)) {
// If there are two typed declarations of the same variable, that
// is an error and the second declaration is ignored, except in the
// case of native types. A null input type means that the declaration
// was made in TypedScopeCreator#createInitialScope and is a
// native type.
if (var.input == null) {
n.setJSType(varType);
if (parent.getType() == Token.VAR) {
if (n.getFirstChild() != null) {
n.getFirstChild().setJSType(varType);
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> } else {
Preconditions.checkState(parent.getType() == Token.FUNCTION);
parent.setJSType(varType);
}
} else {
// Always warn about duplicates if the overridden type does not
// match the original type.
//
// If the types match, suppress the warning iff there was a @suppress
// tag, or if the original declaration was a stub.
if (!(allowDupe ||
var.getParentNode().getType() == Token.EXPR_RESULT) ||
!newType.equals(varType)) {
if (shouldReport) {
compiler.report(
JSError.make(sourceName, n, DUP_VAR_DECLARATION,
variableName, newType.toString(), var.getInputName(),
String.valueOf(var.nameNode.getLineno()),
varType.toString()));
}
}
}
}
}
/**
* Expect that all properties on interfaces that this type implements are
* implemented and correctly typed.
*/
void expectAllInterfaceProperties(NodeTraversal t, Node n,
FunctionType type) {
ObjectType instance = type.getInstanceType();
for (ObjectType implemented : type.getAllImplementedInterfaces()) {
if (implemented.getImplicitPrototype() != null) {
for (String prop :
implemented.getImplicitPrototype().getOwnPropertyNames()) {
expectInterfaceProperty(t, n, instance, implemented, prop);
}
}
}
}
/**
* Expect that the peroperty in an interface that this type implements is
* implemented and correctly typed.
*/
private void expectInterfaceProperty(NodeTraversal t, Node n,
ObjectType instance, ObjectType implementedInterface, String prop) {
if (!instance.hasProperty(prop)) {
// Not implemented
String sourceName = (String) n.getProp(Node.SOURCENAME_PROP);
sourceName = sourceName == null ? "" : sourceName;
if (shouldReport) {
compiler.report(JSError.make(sourceName, n,
INTERFACE_METHOD_NOT_IMPLEMENTED,
prop, implementedInterface.toString(), instance.toString()));
}
registerMismatch(instance, implementedInterface);
} else {
JSType found = instance.getPropertyType(prop);
JSType required
= implementedInterface.getImplicitPrototype().getPropertyType(prop);
found = found.restrictByNotNull
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
}
registerIfMismatch(fnTypeA.getReturnType(), fnTypeB.getReturnType());
}
}
private void registerIfMismatch(JSType found, JSType required) {
if (found != null && required != null &&
!found.canAssignTo(required)) {
registerMismatch(found, required);
}
}
/**
* Formats a found/required error message.
*/
private String formatFoundRequired(String description, JSType found,
JSType required) {
return MessageFormat.format(FOUND_REQUIRED, description, found, required);
}
/**
* Given a node, get a human-readable name for the type of that node so
* that will be easy for the programmer to find the original declaration.
*
* For example, if SubFoo's property "bar" might have the human-readable
* name "Foo.prototype.bar".
*
* @param n The node.
* @param dereference If true, the type of the node will be dereferenced
* to an Object type, if possible.
*/
String getReadableJSTypeName(Node n, boolean dereference) {
// If we're analyzing a GETPROP, the property may be inherited by the
// prototype chain. So climb the prototype chain and find out where
// the property was originally defined.
if (n.getType() == Token.GETPROP) {
ObjectType objectType = getJSType(n.getFirstChild()).dereference();
if (objectType != null) {
String propName = n.getLastChild().getString();
while (objectType != null && !objectType.hasOwnProperty(propName)) {
objectType = objectType.getImplicitPrototype();
}
// Don't show complex function names or anonymous types.
// Instead, try to get a human-readable type name.
if (objectType != null &&
(objectType.getConstructor() != null ||
objectType.isFunctionPrototypeType())) {
return objectType.toString() + "." + propName;
}
}
}
JSType type = getJSType(n);
if (dereference) {
ObjectType dereferenced = type.dereference();
if (dereferenced != null) {
type = dereferenced;
}
}
String qualifiedName = n.getQualifiedName();
if (
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>type.isFunctionPrototypeType() ||
(type.toObjectType() != null &&
type.toObjectType().getConstructor() != null)) {
return type.toString();
} else if (qualifiedName != null) {
return qualifiedName;
} else if (type instanceof FunctionType) {
// Don't show complex function names.
return "function";
} else {
return type.toString();
}
}
/**
* This method gets the JSType from the Node argument and verifies that it is
* present.
*/
private JSType getJSType(Node n) {
JSType jsType = n.getJSType();
if (jsType == null) {
// TODO(user): This branch indicates a compiler bug, not worthy of
// halting the compilation but we should log this and analyze to track
// down why it happens. This is not critical and will be resolved over
// time as the type checker is extended.
return getNativeType(UNKNOWN_TYPE);
} else {
return jsType;
}
}
private JSType getNativeType(JSTypeNative typeId) {
return typeRegistry.getNativeType(typeId);
}
/**
* Signals that the first type and the second type have been
* used interchangeably.
*
* Type-based optimizations should take this into account
* so that they don't wreck code with type warnings.
*/
static class TypeMismatch {
final JSType typeA;
final JSType typeB;
/**
* It's the responsibility of the class that creates the
* {@code TypeMismatch} to ensure that {@code a} and {@code b} are
* non-matching types.
*/
TypeMismatch(JSType a, JSType b) {
this.typeA = a;
this.typeB = b;
}
@Override public boolean equals(Object object) {
if (object instanceof TypeMismatch) {
TypeMismatch that = (TypeMismatch) object;
return (that.typeA.equals(this.typeA) && that.typeB.equals(this.typeB))
|| (that.typeB.equals(this.typeA) && that.typeA.equals(this.typeB));
}
return false;
}
@Override public int hashCode() {
return Objects.
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>() {
return false;
}
@Override
public TernaryValue testForEquality(JSType that) {
TernaryValue result = super.testForEquality(that);
if (result != null) {
return result;
}
if (that.isUnknownType() || that.isSubtype(
getNativeType(JSTypeNative.NUMBER_STRING_BOOLEAN)) ||
that.isObject()) {
return UNKNOWN;
}
return FALSE;
}
@Override
public boolean isBooleanValueType() {
return true;
}
@Override
public boolean matchesNumberContext() {
return true;
}
@Override
public boolean matchesStringContext() {
return true;
}
@Override
public boolean matchesObjectContext() {
// TODO(user): Revisit this for ES4, which is stricter.
return true;
}
@Override
public JSType autoboxesTo() {
return getNativeType(JSTypeNative.BOOLEAN_OBJECT_TYPE);
}
@Override
public String toString() {
return getDisplayName();
}
@Override
public String getDisplayName() {
return "boolean";
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
return BooleanLiteralSet.BOTH;
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseBooleanType();
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>namespace == null) {
namespace = new GlobalNamespace(compiler, root);
}
overrideDefines(collectDefines(root, namespace));
}
private void overrideDefines(Map<String, DefineInfo> allDefines) {
boolean changed = false;
for (Map.Entry<String, DefineInfo> def : allDefines.entrySet()) {
String defineName = def.getKey();
DefineInfo info = def.getValue();
Node inputValue = dominantReplacements.get(defineName);
Node finalValue = inputValue != null ?
inputValue : info.getLastValue();
if (finalValue != info.initialValue) {
info.initialValueParent.replaceChild(
info.initialValue, finalValue.cloneTree());
compiler.addToDebugLog("Overriding @define variable " + defineName);
changed = changed ||
finalValue.getType() != info.initialValue.getType() ||
!finalValue.isEquivalentTo(info.initialValue);
}
}
if (changed) {
compiler.reportCodeChange();
}
Set<String> unusedReplacements = dominantReplacements.keySet();
unusedReplacements.removeAll(allDefines.keySet());
unusedReplacements.removeAll(KNOWN_DEFINES);
for (String unknownDefine : unusedReplacements) {
compiler.report(JSError.make(UNKNOWN_DEFINE_WARNING, unknownDefine));
}
}
private static String format(MessageFormat format, Object... params) {
return format.format(params);
}
/**
* Only defines of literal number, string, or boolean are supported.
*/
private boolean isValidDefineType(JSTypeExpression expression) {
JSType type = expression.evaluate(null, compiler.getTypeRegistry());
return !type.isUnknownType() && type.isSubtype(
compiler.getTypeRegistry().getNativeType(
JSTypeNative.NUMBER_STRING_BOOLEAN));
}
/**
* Finds all defines, and creates a {@link DefineInfo} data structure for
* each one.
* @return A map of {@link DefineInfo} structures, keyed by name.
*/
private Map<String, DefineInfo> collectDefines(Node root,
GlobalNamespace namespace) {
// Find all the global names with a @define annotation
List<Name> allDefines = Lists.newArrayList();
for (Name name : namespace.getName
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>Deque<Integer>();
assignAllowed.push(1);
// Create a map of references to defines keyed by node for easy lookup
allRefInfo = Maps.newHashMap();
for (Name name : listOfDefines) {
if (name.declaration != null) {
allRefInfo.put(name.declaration.node,
new RefInfo(name.declaration, name));
}
if (name.refs != null) {
for (Ref ref : name.refs) {
// If there's a TWIN def, only put one of the twins in.
if (ref.getTwin() == null || !ref.getTwin().isSet()) {
allRefInfo.put(ref.node, new RefInfo(ref, name));
}
}
}
}
}
/**
* Get a map of {@link DefineInfo} structures, keyed by the name of
* the define.
*/
Map<String, DefineInfo> getAllDefines() {
return allDefines;
}
/**
* Keeps track of whether the traversal is in a conditional branch.
* We traverse all nodes of the parse tree.
*/
public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n,
Node parent) {
updateAssignAllowedStack(n, true);
return true;
}
public void visit(NodeTraversal t, Node n, Node parent) {
RefInfo refInfo = allRefInfo.get(n);
if (refInfo != null) {
Ref ref = refInfo.ref;
Name name = refInfo.name;
String fullName = name.fullName();
switch (ref.type) {
case SET_FROM_GLOBAL:
case SET_FROM_LOCAL:
Node valParent = getValueParent(ref);
Node val = valParent.getLastChild();
if (valParent.getType() == Token.ASSIGN && name.isSimpleName() &&
name.declaration == ref) {
// For defines, it's an error if a simple name is assigned
// before it's declared
compiler.report(
t.makeError(val, INVALID_DEFINE_INIT_ERROR, fullName));
} else if (processDefineAssignment(t, fullName, val, valParent)) {
// remove the assignment so that the variable is still declared,
// but no longer assigned to a value, e.g
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
setDefineInfoNotAssignable(info, t);
}
assignableDefines.clear();
}
}
updateAssignAllowedStack(n, false);
}
/**
* Determines whether assignment to a define should be allowed
* in the subtree of the given node, and if not, records that fact.
*
* @param n The node whose subtree we're about to enter or exit.
* @param entering True if we're entering the subtree, false otherwise.
*/
private void updateAssignAllowedStack(Node n, boolean entering) {
switch (n.getType()) {
case Token.CASE:
case Token.FOR:
case Token.FUNCTION:
case Token.HOOK:
case Token.IF:
case Token.SWITCH:
case Token.WHILE:
if (entering) {
assignAllowed.push(0);
} else {
assignAllowed.remove();
}
break;
}
}
/**
* Determines whether assignment to a define should be allowed
* at the current point of the traversal.
*/
private boolean isAssignAllowed() {
return assignAllowed.element() == 1;
}
/**
* Tracks the given define.
*
* @param t The current traversal, for context.
* @param name The full name for this define.
* @param value The value assigned to the define.
* @param valueParent The parent node of value.
* @return Whether we should remove this assignment from the parse tree.
*/
private boolean processDefineAssignment(NodeTraversal t,
String name, Node value, Node valueParent) {
if (value == null || !NodeUtil.isValidDefineValue(value,
allDefines.keySet())) {
compiler.report(
t.makeError(value, INVALID_DEFINE_INIT_ERROR, name));
} else if (!isAssignAllowed()) {
compiler.report(
t.makeError(valueParent, NON_GLOBAL_DEFINE_INIT_ERROR, name));
} else {
DefineInfo info = allDefines.get(name);
if (info == null) {
// First declaration of this define.
info = new DefineInfo(value, valueParent);
allDefines.put(name, info);
assignableDefines.put(name, info);
} else if (info.recordAssignment(value)) {
// The define
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>;
}
/**
* Records the fact that this define can't be assigned a value anymore.
*
* @param reason A message describing the reason why it can't be assigned.
*/
public void setNotAssignable(String reason) {
isAssignable = false;
reasonNotAssignable = reason;
}
/**
* Gets the reason why a define is not assignable.
*/
public String getReasonWhyNotAssignable() {
return reasonNotAssignable;
}
/**
* Records an assigned value.
*
* @return False if there was an error.
*/
public boolean recordAssignment(Node value) {
lastValue = value;
return isAssignable;
}
/**
* Gets the last assigned value.
*/
public Node getLastValue() {
return lastValue;
}
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>.checkNotNull(parent);
if (refinedScope != null && getScope() != refinedScope) {
curNode = node;
pushScope(refinedScope);
traverseBranch(node, parent);
popScope();
} else {
traverseBranch(node, parent);
}
}
/**
* Gets the compiler.
*/
public Compiler getCompiler() {
// TODO(nicksantos): Remove this type cast. This is just temporary
// while refactoring.
return (Compiler) compiler;
}
/**
* Gets the current line number, or zero if it cannot be determined. The line
* number is retrieved lazily as a running time optimization.
*/
public int getLineNumber() {
Node cur = curNode;
while (cur != null) {
int line = cur.getLineno();
if (line >=0) {
return line;
}
cur = cur.getParent();
}
return 0;
}
/**
* Gets the current input source name.
*
* @return A string that may be empty, but not null
*/
public String getSourceName() {
return sourceName;
}
/**
* Gets the current input source.
*/
public CompilerInput getInput() {
return compiler.getInput(sourceName);
}
/**
* Gets the current input module.
*/
public JSModule getModule() {
CompilerInput input = getInput();
return input == null ? null : input.getModule();
}
/** Returns the node currently being traversed. */
public Node getCurrentNode() {
return curNode;
}
/**
* Traverses a node recursively.
*/
public static void traverse(
AbstractCompiler compiler, Node root, Callback cb) {
NodeTraversal t = new NodeTraversal(compiler, cb);
t.traverse(root);
}
/**
* Traverses a list of node trees.
*/
public static void traverseRoots(
AbstractCompiler compiler, List<Node> roots, Callback cb) {
NodeTraversal t = new NodeTraversal(compiler, cb);
t.traverseRoots(roots);
}
/**
* Traverses a branch.
*/
@SuppressWarnings("fallthrough")
private void traverseBranch(Node n, Node parent) {
int type = n.getType();
if (type == Token
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>.SCRIPT) {
sourceName = getSourceName(n);
}
curNode = n;
if (!callback.shouldTraverse(this, n, parent)) return;
switch (type) {
case Token.FUNCTION:
traverseFunction(n, parent);
break;
default:
for (Node child = n.getFirstChild(); child != null; ) {
// child could be replaced, in which case our child node
// would no longer point to the true next
Node next = child.getNext();
traverseBranch(child, n);
child = next;
}
break;
}
curNode = n;
callback.visit(this, n, parent);
}
/**
* Traverses a function.
*/
private void traverseFunction(Node n, Node parent) {
Preconditions.checkState(n.getChildCount() == 3);
Preconditions.checkState(n.getType() == Token.FUNCTION);
final Node fnName = n.getFirstChild();
boolean isFunctionExpression = (parent != null)
&& NodeUtil.isFunctionExpression(n);
if (!isFunctionExpression) {
// Functions declarations are in the scope containing the declaration.
traverseBranch(fnName, n);
}
curNode = n;
pushScope(n);
if (isFunctionExpression) {
// Function expression names are only accessible within the function
// scope.
traverseBranch(fnName, n);
}
final Node args = fnName.getNext();
final Node body = args.getNext();
// Args
traverseBranch(args, n);
// Body
Preconditions.checkState(body.getNext() == null &&
body.getType() == Token.BLOCK);
traverseBranch(body, n);
popScope();
}
/** Examines the functions stack for the last instance of a function node. */
@SuppressWarnings("unchecked")
public Node getEnclosingFunction() {
if (scopes.size() + scopeRoots.size() < 2) {
return null;
} else {
if (scopeRoots.isEmpty()) {
return scopes.peek().getRootNode();
} else {
return scopeRoots.peek();
}
}
}
/** Creates a new scope (e.g. when entering a function). */
private void pushScope(Node node) {
Preconditions.checkState(curNode !=
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> return !(scopes.isEmpty() && scopeRoots.isEmpty());
}
/** Reports a diagnostic (error or warning) */
public void report(Node n, DiagnosticType diagnosticType,
String... arguments) {
JSError error = JSError.make(getSourceName(), n, diagnosticType, arguments);
compiler.report(error);
}
private static String getSourceName(Node n) {
String name = (String) n.getProp(Node.SOURCENAME_PROP);
return name == null ? "" : name;
}
/**
* Creates a JSError during NodeTraversal.
*
* @param n Determines the line and char position within the source file name
* @param type The DiagnosticType
* @param arguments Arguments to be incorporated into the message
*/
public JSError makeError(Node n, CheckLevel level, DiagnosticType type,
String... arguments) {
return JSError.make(getSourceName(), n, level, type, arguments);
}
/**
* Creates a JSError during NodeTraversal.
*
* @param n Determines the line and char position within the source file name
* @param type The DiagnosticType
* @param arguments Arguments to be incorporated into the message
*/
public JSError makeError(Node n, DiagnosticType type, String... arguments) {
return JSError.make(getSourceName(), n, type, arguments);
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> // Output
printInputDelimiter = false;
prettyPrint = false;
lineBreak = false;
reportPath = null;
tracer = TracerMode.OFF;
colorizeErrorOutput = false;
errorFormat = ErrorFormat.SINGLELINE;
warningsGuard = null;
debugFunctionSideEffectsPath = null;
jsOutputFile = "";
externExports = false;
nameReferenceReportPath = null;
nameReferenceGraphPath = null;
// Debugging
aliasHandler = NULL_ALIAS_TRANSFORMATION_HANDLER;
operaCompoundAssignFix = true;
}
/**
* Returns the map of define replacements.
*/
public Map<String, Node> getDefineReplacements() {
return getReplacementsHelper(defineReplacements);
}
/**
* Returns the map of tweak replacements.
*/
public Map<String, Node> getTweakReplacements() {
return getReplacementsHelper(tweakReplacements);
}
/**
* Creates a map of String->Node from a map of String->Number/String/Boolean.
*/
private static Map<String, Node> getReplacementsHelper(
Map<String, Object> source) {
Map<String, Node> map = Maps.newHashMap();
for (Map.Entry<String, Object> entry : source.entrySet()) {
String name = entry.getKey();
Object value = entry.getValue();
if (value instanceof Boolean) {
map.put(name, ((Boolean) value).booleanValue() ?
new Node(Token.TRUE) : new Node(Token.FALSE));
} else if (value instanceof Integer) {
map.put(name, Node.newNumber(((Integer) value).intValue()));
} else if (value instanceof Double) {
map.put(name, Node.newNumber(((Double) value).doubleValue()));
} else {
Preconditions.checkState(value instanceof String);
map.put(name, Node.newString((String) value));
}
}
return map;
}
/**
* Sets the value of the {@code @define} variable in JS
* to a boolean literal.
*/
public void setDefineToBooleanLiteral(String defineName, boolean value) {
defineReplacements.put(defineName, new Boolean(value));
}
/**
* Sets the value of the {@code @define
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>} variable in JS to a
* String literal.
*/
public void setDefineToStringLiteral(String defineName, String value) {
defineReplacements.put(defineName, value);
}
/**
* Sets the value of the {@code @define} variable in JS to a
* number literal.
*/
public void setDefineToNumberLiteral(String defineName, int value) {
defineReplacements.put(defineName, new Integer(value));
}
/**
* Sets the value of the {@code @define} variable in JS to a
* number literal.
*/
public void setDefineToDoubleLiteral(String defineName, double value) {
defineReplacements.put(defineName, new Double(value));
}
/**
* Sets the value of the tweak in JS
* to a boolean literal.
*/
public void setTweakToBooleanLiteral(String tweakId, boolean value) {
tweakReplacements.put(tweakId, new Boolean(value));
}
/**
* Sets the value of the tweak in JS to a
* String literal.
*/
public void setTweakToStringLiteral(String tweakId, String value) {
tweakReplacements.put(tweakId, value);
}
/**
* Sets the value of the tweak in JS to a
* number literal.
*/
public void setTweakToNumberLiteral(String tweakId, int value) {
tweakReplacements.put(tweakId, new Integer(value));
}
/**
* Sets the value of the tweak in JS to a
* number literal.
*/
public void setTweakToDoubleLiteral(String tweakId, double value) {
tweakReplacements.put(tweakId, new Double(value));
}
/**
* Skip all possible passes, to make the compiler as fast as possible.
*/
public void skipAllCompilerPasses() {
skipAllPasses = true;
}
/**
* Whether the warnings guard in this Options object enables the given
* group of warnings.
*/
boolean enables(DiagnosticGroup type) {
return warningsGuard != null && warningsGuard.enables(type);
}
/**
* Whether the warnings guard in this Options object disables the given
* group of warnings.
*/
boolean disables(DiagnosticGroup type) {
return warningsGuard != null && warningsGuard.disable
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>s(type);
}
/**
* Configure the given type of warning to the given level.
*/
public void setWarningLevel(DiagnosticGroup type, CheckLevel level) {
addWarningsGuard(new DiagnosticGroupWarningsGuard(type, level));
}
WarningsGuard getWarningsGuard() {
return warningsGuard;
}
/**
* Add a guard to the set of warnings guards.
*/
public void addWarningsGuard(WarningsGuard guard) {
if (warningsGuard == null) {
warningsGuard = new ComposeWarningsGuard(guard);
} else {
warningsGuard.addGuard(guard);
}
}
/**
* Sets the variable and property renaming policies for the compiler,
* in a way that clears warnings about the renaming policy being
* uninitialized from flags.
*/
public void setRenamingPolicy(VariableRenamingPolicy newVariablePolicy,
PropertyRenamingPolicy newPropertyPolicy) {
this.variableRenaming = newVariablePolicy;
this.propertyRenaming = newPropertyPolicy;
}
public void setPropertyAffinity(boolean useAffinity) {
this.propertyAffinity = useAffinity;
}
/** Should shadow outer scope variable name during renaming. */
public void setShadowVariables(boolean shadow) {
this.shadowVariables = shadow;
}
/**
* If true, flattens multi-level property names on extern types
* (e.g. String$f = x). This should only be used with the typed version of
* the externs files.
*/
public void setCollapsePropertiesOnExternTypes(boolean collapse) {
collapsePropertiesOnExternTypes = collapse;
}
/**
* If true, process goog.testing.ObjectPropertyString instances.
*/
public void setProcessObjectPropertyString(boolean process) {
processObjectPropertyString = process;
}
/**
* Sets the id generators to replace.
*/
public void setIdGenerators(Set<String> idGenerators) {
this.idGenerators = Sets.newHashSet(idGenerators);
}
/**
* Sets the functions whose debug strings to replace.
*/
public void setReplaceStringsConfiguration(
String placeholderToken, List<String> functionDescriptors) {
this.replaceStringsPlaceholderToken = placeholderToken;
this.replaceStringsFunctionDescriptions =
Lists.newArrayList(functionDescriptors);
}
public void setRewriteNewDateG
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>oogNow(boolean rewrite) {
this.rewriteNewDateGoogNow = rewrite;
}
public void setRemoveAbstractMethods(boolean remove) {
this.removeAbstractMethods = remove;
}
public void setRemoveClosureAsserts(boolean remove) {
this.removeClosureAsserts = remove;
}
/**
* If true, name anonymous functions only. All other passes will be skipped.
*/
public void setNameAnonymousFunctionsOnly(boolean value) {
this.nameAnonymousFunctionsOnly = value;
}
public void lineLengthThreshold(int value) {
this.lineLengthThreshold = value;
}
public void setColorizeErrorOutput(boolean colorizeErrorOutput) {
this.colorizeErrorOutput = colorizeErrorOutput;
}
public boolean shouldColorizeErrorOutput() {
return colorizeErrorOutput;
}
/**
* If true, chain calls to functions that return this.
*/
public void setChainCalls(boolean value) {
this.chainCalls = value;
}
/**
* If true, accept `const' keyword.
*/
public void setAcceptConstKeyword(boolean value) {
this.acceptConstKeyword = value;
}
/**
* Enable runtime type checking, which adds JS type assertions for debugging.
*
* @param logFunction A JS function to be used for logging runtime type
* assertion failures.
*/
public void enableRuntimeTypeCheck(String logFunction) {
this.runtimeTypeCheck = true;
this.runtimeTypeCheckLogFunction = logFunction;
}
public void disableRuntimeTypeCheck() {
this.runtimeTypeCheck = false;
}
public void setGenerateExports(boolean generateExports) {
this.generateExports = generateExports;
}
public void setCodingConvention(CodingConvention codingConvention) {
this.codingConvention = codingConvention;
}
public CodingConvention getCodingConvention() {
return codingConvention;
}
/**
* Sort inputs by their goog.provide/goog.require calls, and prune inputs
* whose symbols are not required.
*/
public void setManageClosureDependencies(boolean newVal) {
manageClosureDependencies = newVal;
}
/**
* Sort inputs by their goog.provide/goog.require calls.
*
* @param entryPoints Entry points to the program. Must be goog.provide'd
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import static com.google.javascript.rhino.jstype.TernaryValue.FALSE;
import static com.google.javascript.rhino.jstype.TernaryValue.UNKNOWN;
/**
* String type.
*/
public final class StringType extends ValueType {
private static final long serialVersionUID = 1L;
StringType(JSTypeRegistry registry) {
super(registry);
}
@Override
public Ternary
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>Value testForEquality(JSType that) {
TernaryValue result = super.testForEquality(that);
if (result != null) {
return result;
}
if (that.isUnknownType() || that.isSubtype(
getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN))) {
return UNKNOWN;
}
return FALSE;
}
@Override
public boolean isStringValueType() {
return true;
}
@Override
public boolean matchesNumberContext() {
return true;
}
@Override
public boolean matchesStringContext() {
return true;
}
@Override
public boolean matchesObjectContext() {
// TODO(user): Revisit this for ES4, which is stricter.
return true;
}
@Override
public String toString() {
return getDisplayName();
}
@Override
public String getDisplayName() {
return "string";
}
@Override
public JSType autoboxesTo() {
return getNativeType(JSTypeNative.STRING_OBJECT_TYPE);
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
return BooleanLiteralSet.BOTH;
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseStringType();
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> The order that the guards are applied in.
private final TreeSet<WarningsGuard> guards =
new TreeSet<WarningsGuard>(guardComparator);
public ComposeWarningsGuard(List<WarningsGuard> guards) {
addGuards(guards);
}
public ComposeWarningsGuard(WarningsGuard... guards) {
this(Lists.newArrayList(guards));
}
void addGuard(WarningsGuard guard) {
if (guard instanceof ComposeWarningsGuard) {
// Reverse the guards, so that they have the same order in the result.
addGuards(((ComposeWarningsGuard) guard).guards.descendingSet());
} else {
numberOfAdds++;
orderOfAddition.put(guard, numberOfAdds);
guards.remove(guard);
guards.add(guard);
}
}
private void addGuards(Iterable<WarningsGuard> guards) {
for (WarningsGuard guard : guards) {
addGuard(guard);
}
}
@Override
public CheckLevel level(JSError error) {
for (WarningsGuard guard : guards) {
CheckLevel newLevel = guard.level(error);
if (newLevel != null) {
return newLevel;
}
}
return null;
}
@Override
public boolean disables(DiagnosticGroup group) {
nextSingleton:
for (DiagnosticType type : group.getTypes()) {
DiagnosticGroup singleton = DiagnosticGroup.forType(type);
for (WarningsGuard guard : guards) {
if (guard.disables(singleton)) {
continue nextSingleton;
} else if (guard.enables(singleton)) {
return false;
}
}
return false;
}
return true;
}
/**
* Determines whether this guard will "elevate" the status of any disabled
* diagnostic type in the group to a warning or an error.
*/
@Override
public boolean enables(DiagnosticGroup group) {
for (WarningsGuard guard : guards) {
if (guard.enables(group)) {
return true;
} else if (guard.disables(group)) {
return false;
}
}
return false;
}
List<WarningsGuard> getGuards() {
return Collections.unmodifiableList(Lists.newArrayList(guards));
}
@Override
public String
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> }
/**
* Generates a list of modules from a list of inputs, such that modules
* form a bush formation. In a bush formation, module 2 depends
* on module 1, and all other modules depend on module 2.
*/
static JSModule[] createModuleBush(String ... inputs) {
Preconditions.checkState(inputs.length > 2);
JSModule[] modules = createModules(inputs);
for (int i = 1; i < modules.length; i++) {
modules[i].addDependency(modules[i == 1 ? 0 : 1]);
}
return modules;
}
/**
* Generates a list of modules from a list of inputs, such that modules
* form a tree formation. In a tree formation, module N depends on
* module `floor(N/2)`, So the modules form a balanced binary tree.
*/
static JSModule[] createModuleTree(String ... inputs) {
JSModule[] modules = createModules(inputs);
for (int i = 1; i < modules.length; i++) {
modules[i].addDependency(modules[(i - 1) / 2]);
}
return modules;
}
/**
* Generates a list of modules from a list of inputs. Does not generate any
* dependencies between the modules.
*/
static JSModule[] createModules(String... inputs) {
JSModule[] modules = new JSModule[inputs.length];
for (int i = 0; i < inputs.length; i++) {
JSModule module = modules[i] = new JSModule("m" + i);
module.add(JSSourceFile.fromCode("i" + i, inputs[i]));
}
return modules;
}
private static class BlackHoleErrorManager extends BasicErrorManager {
private BlackHoleErrorManager(Compiler compiler) {
compiler.setErrorManager(this);
}
@Override
public void println(CheckLevel level, JSError error) {}
@Override
public void printSummary() {}
}
Compiler createCompiler() {
Compiler compiler = new Compiler();
return compiler;
}
protected void setExpectedSymbolTableError(DiagnosticType type) {
this.expectedSymbolTableError = type;
}
/** Finds the first matching qualified name node in post-traversal order.
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> unknown types in JSTypeNative.
private final boolean isChecked;
UnknownType(JSTypeRegistry registry, boolean isChecked) {
super(registry);
this.isChecked = isChecked;
}
@Override
public boolean isUnknownType() {
return true;
}
@Override
public boolean isCheckedUnknownType() {
return isChecked;
}
@Override
public boolean canAssignTo(JSType that) {
return true;
}
@Override
public boolean canBeCalled() {
return true;
}
@Override
public boolean matchesNumberContext() {
return true;
}
@Override
public boolean matchesObjectContext() {
return true;
}
@Override
public boolean matchesStringContext() {
return true;
}
@Override
public TernaryValue testForEquality(JSType that) {
return UNKNOWN;
}
@Override
public boolean isNullable() {
return true;
}
@Override
public boolean isSubtype(JSType that) {
return true;
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseUnknownType();
}
@Override
public String toString() {
return getReferenceName();
}
@Override
boolean defineProperty(String propertyName, JSType type,
boolean inferred, boolean inExterns, Node propertyNode) {
// nothing to define
return true;
}
@Override
public ObjectType getImplicitPrototype() {
return null;
}
@Override
public int getPropertiesCount() {
return Integer.MAX_VALUE;
}
@Override
void collectPropertyNames(Set<String> props) {
}
@Override
public JSType getPropertyType(String propertyName) {
return this;
}
@Override
public boolean hasProperty(String propertyName) {
return true;
}
@Override
public FunctionType getConstructor() {
return null;
}
@Override
public String getReferenceName() {
return isChecked ? "??" : "?";
}
@Override
public String getDisplayName() {
return "Unknown";
}
@Override
public boolean hasDisplayName() {
return true;
}
@Override
public boolean isPropertyTypeDeclared(String propertyName) {
return false;
}
@Override
public boolean isPropertyTypeInferred(
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> serialVersionUID = 1L;
VoidType(JSTypeRegistry registry) {
super(registry);
}
@Override
public JSType restrictByNotNullOrUndefined() {
return registry.getNativeType(JSTypeNative.NO_TYPE);
}
@Override
public TernaryValue testForEquality(JSType that) {
if (UNKNOWN.equals(super.testForEquality(that))) {
return UNKNOWN;
}
if (that.isSubtype(this) ||
that.isSubtype(getNativeType(JSTypeNative.NULL_TYPE))) {
return TRUE;
}
return FALSE;
}
@Override
public boolean matchesNumberContext() {
return false;
}
@Override
public boolean matchesObjectContext() {
return false;
}
@Override
public boolean matchesStringContext() {
return true;
}
@Override
public boolean isVoidType() {
return true;
}
@Override
public String toString() {
return getDisplayName();
}
@Override
public String getDisplayName() {
return "undefined";
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
return BooleanLiteralSet.FALSE;
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseVoidType();
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> link.
* If there is no next link, returns the blind scope.
*/
protected FlowScope nextPreciserScopeKnowingConditionOutcome(Node condition,
FlowScope blindScope, boolean outcome) {
return nextLink != null ? nextLink.getPreciserScopeKnowingConditionOutcome(
condition, blindScope, outcome) : blindScope;
}
/**
* Returns the type of a node in the given scope if the node corresponds to a
* name whose type is capable of being refined.
* @return The current type of the node if it can be refined, null otherwise.
*/
JSType getTypeIfRefinable(Node node, FlowScope scope) {
switch (node.getType()) {
case Token.NAME:
StaticSlot<JSType> nameVar = scope.getSlot(node.getString());
if (nameVar != null) {
JSType nameVarType = nameVar.getType();
if (nameVarType == null) {
nameVarType = node.getJSType();
}
return nameVarType;
}
return null;
case Token.GETPROP:
String qualifiedName = node.getQualifiedName();
if (qualifiedName == null) {
return null;
}
StaticSlot<JSType> propVar = scope.getSlot(qualifiedName);
JSType propVarType = null;
if (propVar != null) {
propVarType = propVar.getType();
}
if (propVarType == null) {
propVarType = node.getJSType();
}
if (propVarType == null) {
propVarType = getNativeType(UNKNOWN_TYPE);
}
return propVarType;
}
return null;
}
/**
* Declares a refined type in {@code scope} for the name represented by
* {@code node}. It must be possible to refine the type of the given node in
* the given scope, as determined by {@link #getTypeIfRefinable}.
*/
protected void declareNameInScope(FlowScope scope, Node node, JSType type) {
switch (node.getType()) {
case Token.NAME:
scope.inferSlotType(node.getString(), type);
break;
case Token.GETPROP:
String qualifiedName = node.getQualifiedName();
Preconditions.checkNotNull
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>(qualifiedName);
JSType origType = node.getJSType();
origType = origType == null ? getNativeType(UNKNOWN_TYPE) : origType;
scope.inferQualifiedSlot(qualifiedName, origType, type);
break;
default:
throw new IllegalArgumentException("Node cannot be refined. \n" +
node.toStringTree());
}
}
/**
* @see #getRestrictedWithoutUndefined(JSType)
*/
private final Visitor<JSType> restrictUndefinedVisitor =
new Visitor<JSType>() {
public JSType caseEnumElementType(EnumElementType enumElementType) {
JSType type = enumElementType.getPrimitiveType().visit(this);
if (type != null && enumElementType.getPrimitiveType().equals(type)) {
return enumElementType;
} else {
return type;
}
}
public JSType caseAllType() {
return typeRegistry.createUnionType(OBJECT_TYPE, NUMBER_TYPE,
STRING_TYPE, BOOLEAN_TYPE, NULL_TYPE);
}
public JSType caseNoObjectType() {
return getNativeType(NO_OBJECT_TYPE);
}
public JSType caseNoType() {
return getNativeType(NO_TYPE);
}
public JSType caseBooleanType() {
return getNativeType(BOOLEAN_TYPE);
}
public JSType caseFunctionType(FunctionType type) {
return type;
}
public JSType caseNullType() {
return getNativeType(NULL_TYPE);
}
public JSType caseNumberType() {
return getNativeType(NUMBER_TYPE);
}
public JSType caseObjectType(ObjectType type) {
return type;
}
public JSType caseStringType() {
return getNativeType(STRING_TYPE);
}
public JSType caseUnionType(UnionType type) {
return type.getRestrictedUnion(getNativeType(VOID_TYPE));
}
public JSType caseUnknownType() {
return getNativeType(UNKNOWN_TYPE);
}
public JSType caseVoidType() {
return null;
}
};
/**
* @see #getRestrictedWithoutNull(JSType)
*/
private final Visitor<JSType> restrictNullVisitor =
new Visitor<JSType>() {
public JSType caseEnumElementType(EnumElementType enumElementType) {
JSType type =
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> enumElementType.getPrimitiveType().visit(this);
if (type != null && enumElementType.getPrimitiveType().equals(type)) {
return enumElementType;
} else {
return type;
}
}
public JSType caseAllType() {
return typeRegistry.createUnionType(OBJECT_TYPE, NUMBER_TYPE,
STRING_TYPE, BOOLEAN_TYPE, VOID_TYPE);
}
public JSType caseNoObjectType() {
return getNativeType(NO_OBJECT_TYPE);
}
public JSType caseNoType() {
return getNativeType(NO_TYPE);
}
public JSType caseBooleanType() {
return getNativeType(BOOLEAN_TYPE);
}
public JSType caseFunctionType(FunctionType type) {
return type;
}
public JSType caseNullType() {
return null;
}
public JSType caseNumberType() {
return getNativeType(NUMBER_TYPE);
}
public JSType caseObjectType(ObjectType type) {
return type;
}
public JSType caseStringType() {
return getNativeType(STRING_TYPE);
}
public JSType caseUnionType(UnionType type) {
return type.getRestrictedUnion(getNativeType(NULL_TYPE));
}
public JSType caseUnknownType() {
return getNativeType(UNKNOWN_TYPE);
}
public JSType caseVoidType() {
return getNativeType(VOID_TYPE);
}
};
/**
* A class common to all visitors that need to restrict the type based on
* {@code typeof}-like conditions.
*/
abstract class RestrictByTypeOfResultVisitor
implements Visitor<JSType> {
/**
* Abstracts away the similarities between visiting the unknown type and the
* all type.
* @param topType {@code UNKNOWN_TYPE} or {@code ALL_TYPE}
* @return the restricted type
* @see #caseAllType
* @see #caseUnknownType
*/
protected abstract JSType caseTopType(JSType topType);
public JSType caseAllType() {
return caseTopType(getNativeType(ALL_TYPE));
}
public JSType caseUnknownType() {
return caseTopType(getNativeType(UNKNOWN_TYPE));
}
public JSType caseUnionType(UnionType type) {
JSType restricted
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> = null;
for (JSType alternate : type.getAlternates()) {
JSType restrictedAlternate = alternate.visit(this);
if (restrictedAlternate != null) {
if (restricted == null) {
restricted = restrictedAlternate;
} else {
restricted = restrictedAlternate.getLeastSupertype(restricted);
}
}
}
return restricted;
}
public JSType caseNoType() {
return getNativeType(NO_TYPE);
}
public JSType caseEnumElementType(EnumElementType enumElementType) {
// NOTE(nicksantos): This is a white lie. Suppose we have:
// /** @enum {string|number} */ var MyEnum = ...;
// if (goog.isNumber(myEnumInstance)) {
// /* what is myEnumInstance here? */
// }
// There is no type that represents {MyEnum - string}. What we really
// need is a notion of "enum subtyping", so that we could dynamically
// create a subtype of MyEnum restricted by string. In any case,
// this should catch the common case.
JSType type = enumElementType.getPrimitiveType().visit(this);
if (type != null && enumElementType.getPrimitiveType().equals(type)) {
return enumElementType;
} else {
return type;
}
}
}
/**
* A class common to all visitors that need to restrict the type based on
* some {@code typeof}-like condition being true. All base cases return
* {@code null}. It is up to the subclasses to override the appropriate ones.
*/
abstract class RestrictByTrueTypeOfResultVisitor
extends RestrictByTypeOfResultVisitor {
public JSType caseNoObjectType() {
return null;
}
public JSType caseBooleanType() {
return null;
}
public JSType caseFunctionType(FunctionType type) {
return null;
}
public JSType caseNullType() {
return null;
}
public JSType caseNumberType() {
return null;
}
public JSType caseObjectType(ObjectType type) {
return null;
}
public JSType caseStringType() {
return null;
}
public JSType caseVoidType() {
return null;
}
}
/**
* A class common to all visitors that need to restrict the type based on
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
* some {@code typeof}-like condition being false. All base cases return
* their type. It is up to the subclasses to override the appropriate ones.
*/
abstract class RestrictByFalseTypeOfResultVisitor
extends RestrictByTypeOfResultVisitor {
@Override
protected JSType caseTopType(JSType topType) {
return topType;
}
public JSType caseNoObjectType() {
return getNativeType(NO_OBJECT_TYPE);
}
public JSType caseBooleanType() {
return getNativeType(BOOLEAN_TYPE);
}
public JSType caseFunctionType(FunctionType type) {
return type;
}
public JSType caseNullType() {
return getNativeType(NULL_TYPE);
}
public JSType caseNumberType() {
return getNativeType(NUMBER_TYPE);
}
public JSType caseObjectType(ObjectType type) {
return type;
}
public JSType caseStringType() {
return getNativeType(STRING_TYPE);
}
public JSType caseVoidType() {
return getNativeType(VOID_TYPE);
}
}
/**
* @see ChainableReverseAbstractInterpreter#getRestrictedByTypeOfResult
*/
private class RestrictByOneTypeOfResultVisitor
extends RestrictByTypeOfResultVisitor {
/**
* A value known to be equal or not equal to the result of the
* {@code typeOf} operation.
*/
private final String value;
/**
* {@code true} if the {@code typeOf} result is known to equal
* {@code value}; {@code false} if it is known <em>not</em> to equal
* {@code value}.
*/
private final boolean resultEqualsValue;
RestrictByOneTypeOfResultVisitor(String value, boolean resultEqualsValue) {
this.value = value;
this.resultEqualsValue = resultEqualsValue;
}
/**
* Computes whether the given result of a {@code typeof} operator matches
* expectations, i.e. whether a type that gives such a result should be
* kept.
*/
private boolean matchesExpectation(String result) {
return result.equals(value) == resultEqualsValue;
}
@Override
protected JSType caseTopType(JSType topType) {
JSType result = topType;
if (resultEqualsValue) {
JSType typeByName
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> = getNativeTypeForTypeOf(value);
if (typeByName != null) {
result = typeByName;
}
}
return result;
}
public JSType caseNoObjectType() {
return (value.equals("object") || value.equals("function")) ==
resultEqualsValue ? getNativeType(NO_OBJECT_TYPE) : null;
}
public JSType caseBooleanType() {
return matchesExpectation("boolean") ? getNativeType(BOOLEAN_TYPE) : null;
}
public JSType caseFunctionType(FunctionType type) {
return matchesExpectation("function") ? type : null;
}
public JSType caseNullType() {
return matchesExpectation("object") ? getNativeType(NULL_TYPE) : null;
}
public JSType caseNumberType() {
return matchesExpectation("number") ? getNativeType(NUMBER_TYPE) : null;
}
public JSType caseObjectType(ObjectType type) {
if (value.equals("function")) {
JSType ctorType = getNativeType(U2U_CONSTRUCTOR_TYPE);
return resultEqualsValue && ctorType.isSubtype(type) ? ctorType : null;
}
return matchesExpectation("object") ? type : null;
}
public JSType caseStringType() {
return matchesExpectation("string") ? getNativeType(STRING_TYPE) : null;
}
public JSType caseVoidType() {
return matchesExpectation("undefined") ? getNativeType(VOID_TYPE) : null;
}
}
/**
* Returns a version of type where undefined is not present.
*/
final JSType getRestrictedWithoutUndefined(JSType type) {
return type == null ? null : type.visit(restrictUndefinedVisitor);
}
/**
* Returns a version of type where null is not present.
*/
final JSType getRestrictedWithoutNull(JSType type) {
return type == null ? null : type.visit(restrictNullVisitor);
}
/**
* Returns a version of {@code type} that is restricted by some knowledge
* about the result of the {@code typeof} operation.
* <p>
* The behavior of the {@code typeof} operator can be summarized by the
* following table:
* <table>
* <tr><th>type</th><th>result</th></tr>
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> * <tr><td>{@code undefined}</td><td>"undefined"</td></tr>
* <tr><td>{@code null}</td><td>"object"</td></tr>
* <tr><td>{@code boolean}</td><td>"boolean"</td></tr>
* <tr><td>{@code number}</td><td>"number"</td></tr>
* <tr><td>{@code string}</td><td>"string"</td></tr>
* <tr><td>{@code Object} (which doesn't implement [[Call]])</td>
* <td>"object"</td></tr>
* <tr><td>{@code Object} (which implements [[Call]])</td>
* <td>"function"</td></tr>
* </table>
* @param type the type to restrict
* @param value A value known to be equal or not equal to the result of the
* {@code typeof} operation
* @param resultEqualsValue {@code true} if the {@code typeOf} result is known
* to equal {@code value}; {@code false} if it is known <em>not</em> to
* equal {@code value}
* @return the restricted type or null if no version of the type matches the
* restriction
*/
JSType getRestrictedByTypeOfResult(JSType type, String value,
boolean resultEqualsValue) {
if (type == null) {
if (resultEqualsValue) {
JSType result = getNativeTypeForTypeOf(value);
return result == null ? getNativeType(UNKNOWN_TYPE) : result;
} else {
return null;
}
}
return type.visit(
new RestrictByOneTypeOfResultVisitor(value, resultEqualsValue));
}
JSType getNativeType(JSTypeNative typeId) {
return typeRegistry.getNativeType(typeId);
}
/**
* If we definitely know what a type is based on the typeof result,
* return it. Otherwise, return null.
*
* The typeof operation in JS is poorly defined, and this function works
* for both the native typeof and goog.typeOf. It should not be made public,
* because its semantics are informally defined, and would be wrong in
* the general case.
*/
private JSType getNativeTypeForTypeOf(
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>String value) {
if (value.equals("number")) {
return getNativeType(NUMBER_TYPE);
} else if (value.equals("boolean")) {
return getNativeType(BOOLEAN_TYPE);
} else if (value.equals("string")) {
return getNativeType(STRING_TYPE);
} else if (value.equals("undefined")) {
return getNativeType(VOID_TYPE);
} else if (value.equals("function")) {
return getNativeType(U2U_CONSTRUCTOR_TYPE);
} else {
return null;
}
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
ensureTyped(t, n);
break;
case Token.GET_REF:
ensureTyped(t, n, getJSType(n.getFirstChild()));
break;
case Token.NULL:
ensureTyped(t, n, NULL_TYPE);
break;
case Token.NUMBER:
ensureTyped(t, n, NUMBER_TYPE);
break;
case Token.STRING:
// Object literal keys are handled with OBJECTLIT
if (!NodeUtil.isObjectLitKey(n, n.getParent())) {
ensureTyped(t, n, STRING_TYPE);
}
break;
case Token.GET:
case Token.SET:
// Object literal keys are handled with OBJECTLIT
break;
case Token.ARRAYLIT:
ensureTyped(t, n, ARRAY_TYPE);
break;
case Token.REGEXP:
ensureTyped(t, n, REGEXP_TYPE);
break;
case Token.GETPROP:
visitGetProp(t, n, parent);
typeable = !(parent.getType() == Token.ASSIGN &&
parent.getFirstChild() == n);
break;
case Token.GETELEM:
visitGetElem(t, n);
// The type of GETELEM is always unknown, so no point counting that.
// If that unknown leaks elsewhere (say by an assignment to another
// variable), then it will be counted.
typeable = false;
break;
case Token.VAR:
visitVar(t, n);
typeable = false;
break;
case Token.NEW:
visitNew(t, n);
typeable = true;
break;
case Token.CALL:
visitCall(t, n);
typeable = !NodeUtil.isExpressionNode(parent);
break;
case Token.RETURN:
visitReturn(t, n);
typeable = false;
break;
case Token.DEC:
case Token.INC:
left = n.getFirstChild();
validator.expectNumber(
t, left, getJSType(left), "increment/decrement");
ensureTyped(t, n, NUMBER_TYPE);
break;
case Token.NOT:
ensureTyped(t, n, BOOLEAN_TYPE);
break;
case Token.VOID:
ensureTyped(t, n, VOID_TYPE);
break;
case
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> Token.TYPEOF:
ensureTyped(t, n, STRING_TYPE);
break;
case Token.BITNOT:
childType = getJSType(n.getFirstChild());
if (!childType.matchesInt32Context()) {
report(t, n, BIT_OPERATION, NodeUtil.opToStr(n.getType()),
childType.toString());
}
ensureTyped(t, n, NUMBER_TYPE);
break;
case Token.POS:
case Token.NEG:
left = n.getFirstChild();
validator.expectNumber(t, left, getJSType(left), "sign operator");
ensureTyped(t, n, NUMBER_TYPE);
break;
case Token.EQ:
case Token.NE: {
leftType = getJSType(n.getFirstChild());
rightType = getJSType(n.getLastChild());
JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined();
JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined();
TernaryValue result =
leftTypeRestricted.testForEquality(rightTypeRestricted);
if (result != TernaryValue.UNKNOWN) {
if (n.getType() == Token.NE) {
result = result.not();
}
report(t, n, DETERMINISTIC_TEST, leftType.toString(),
rightType.toString(), result.toString());
}
ensureTyped(t, n, BOOLEAN_TYPE);
break;
}
case Token.SHEQ:
case Token.SHNE: {
leftType = getJSType(n.getFirstChild());
rightType = getJSType(n.getLastChild());
JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined();
JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined();
if (!leftTypeRestricted.canTestForShallowEqualityWith(
rightTypeRestricted)) {
report(t, n, DETERMINISTIC_TEST_NO_RESULT, leftType.toString(),
rightType.toString());
}
ensureTyped(t, n, BOOLEAN_TYPE);
break;
}
case Token.LT:
case Token.LE:
case Token.GT:
case Token.GE:
leftType = getJSType(n.getFirstChild());
rightType = getJSType(n
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> Token.OR:
if (n.getJSType() != null) { // If we didn't run type inference.
ensureTyped(t, n);
} else {
// If this is an enum, then give that type to the objectlit as well.
if ((n.getType() == Token.OBJECTLIT)
&& (parent.getJSType() instanceof EnumType)) {
ensureTyped(t, n, parent.getJSType());
} else {
ensureTyped(t, n);
}
}
if (n.getType() == Token.OBJECTLIT) {
for (Node key : n.children()) {
visitObjLitKey(t, key, n);
}
}
break;
default:
report(t, n, UNEXPECTED_TOKEN, Token.name(n.getType()));
ensureTyped(t, n);
break;
}
// Don't count externs since the user's code may not even use that part.
typeable = typeable && !inExterns;
if (typeable) {
doPercentTypedAccounting(t, n);
}
checkNoTypeCheckSection(n, false);
}
/**
* Counts the given node in the typed statistics.
* @param n a node that should be typed
*/
private void doPercentTypedAccounting(NodeTraversal t, Node n) {
JSType type = n.getJSType();
if (type == null) {
nullCount++;
} else if (type.isUnknownType()) {
if (reportUnknownTypes.isOn()) {
compiler.report(
t.makeError(n, reportUnknownTypes, UNKNOWN_EXPR_TYPE));
}
unknownCount++;
} else {
typedCount++;
}
}
/**
* Visits an assignment <code>lvalue = rvalue</code>. If the
* <code>lvalue</code> is a prototype modification, we change the schema
* of the object type it is referring to.
* @param t the traversal
* @param assign the assign node
* (<code>assign.getType() == Token.ASSIGN</code> is an implicit invariant)
*/
private void visitAssign(NodeTraversal t, Node assign) {
JSDocInfo info = assign.getJSDocInfo();
Node lvalue = assign.
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>Type.isInterface()) {
checkDeclaredPropertyInheritance(
t, assign, functionType, property, info, getJSType(rvalue));
}
} else {
// TODO(user): might want to flag that
}
return;
}
}
// object.property = ...;
ObjectType type = ObjectType.cast(
objectJsType.restrictByNotNullOrUndefined());
if (type != null) {
if (type.hasProperty(property) &&
!type.isPropertyTypeInferred(property) &&
!propertyIsImplicitCast(type, property)) {
validator.expectCanAssignToPropertyOf(
t, assign, getJSType(rvalue),
type.getPropertyType(property), object, property);
}
return;
}
} else if (lvalue.getType() == Token.NAME) {
// variable with inferred type case
JSType rvalueType = getJSType(assign.getLastChild());
Var var = t.getScope().getVar(lvalue.getString());
if (var != null) {
if (var.isTypeInferred()) {
return;
}
}
}
// fall through case
JSType leftType = getJSType(lvalue);
Node rightChild = assign.getLastChild();
JSType rightType = getJSType(rightChild);
if (validator.expectCanAssignTo(
t, assign, rightType, leftType, "assignment")) {
ensureTyped(t, assign, rightType);
} else {
ensureTyped(t, assign);
}
}
/**
* Visits an object literal field definition <code>key : value</code>.
*
* If the <code>lvalue</code> is a prototype modification, we change the
* schema of the object type it is referring to.
*
* @param t the traversal
* @param key the assign node
*/
private void visitObjLitKey(NodeTraversal t, Node key, Node objlit) {
// TODO(johnlenz): Validate get and set function declarations are valid
// as is the functions can have "extraneous" bits.
// For getter and setter property definitions the
// rvalue type != the property type.
Node rvalue = key.getFirstChild();
JSType rightType = NodeUtil.getObjectLitKeyTypeFromValueType(
key,
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> getJSType(rvalue));
if (rightType == null) {
rightType = getNativeType(UNKNOWN_TYPE);
}
Node owner = objlit;
// Validate value is assignable to the key type.
JSType keyType = getJSType(key);
boolean valid = validator.expectCanAssignToPropertyOf(t, key,
rightType, keyType,
owner, NodeUtil.getObjectLitKeyName(key));
if (valid) {
ensureTyped(t, key, rightType);
} else {
ensureTyped(t, key);
}
// Validate that the key type is assignable to the object property type.
// This is necessary as the objlit may have been cast to a non-literal
// object type.
// TODO(johnlenz): consider introducing a CAST node to the AST (or
// perhaps a parentheses node).
JSType objlitType = getJSType(objlit);
ObjectType type = ObjectType.cast(
objlitType.restrictByNotNullOrUndefined());
if (type != null) {
String property = NodeUtil.getObjectLitKeyName(key);
if (type.hasProperty(property) &&
!type.isPropertyTypeInferred(property) &&
!propertyIsImplicitCast(type, property)) {
validator.expectCanAssignToPropertyOf(
t, key, keyType,
type.getPropertyType(property), owner, property);
}
return;
}
}
/**
* Returns true if any type in the chain has an implictCast annotation for
* the given property.
*/
private boolean propertyIsImplicitCast(ObjectType type, String prop) {
for (; type != null; type = type.getImplicitPrototype()) {
JSDocInfo docInfo = type.getOwnPropertyJSDocInfo(prop);
if (docInfo != null && docInfo.isImplicitCast()) {
return true;
}
}
return false;
}
/**
* Given a constructor type and a property name, check that the property has
* the JSDoc annotation @override iff the property is declared on a
* superclass. Several checks regarding inheritance correctness are also
* performed.
*/
private void checkDeclaredPropertyInheritance(
NodeTraversal t, Node n, FunctionType ctorType, String propertyName,
JSDocInfo info, JSType propertyType
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>)
? ", or " + abstractMethodName
: "";
compiler.report(
t.makeError(object, INVALID_INTERFACE_MEMBER_DECLARATION,
abstractMethodMessage));
}
if (assign.getLastChild().getType() == Token.FUNCTION
&& !NodeUtil.isEmptyBlock(assign.getLastChild().getLastChild())) {
compiler.report(
t.makeError(object, INTERFACE_FUNCTION_NOT_EMPTY,
abstractMethodName));
}
}
/**
* Visits an ASSIGN node for cases such as
* <pre>
* object.property = ...;
* </pre>
* that have an {@code @type} annotation.
*/
private void visitAnnotatedAssignGetprop(NodeTraversal t,
Node assign, JSType type, Node object, String property, Node rvalue) {
// verifying that the rvalue has the correct type
validator.expectCanAssignToPropertyOf(t, assign, getJSType(rvalue), type,
object, property);
}
/**
* Visits a NAME node.
*
* @param t The node traversal object that supplies context, such as the
* scope chain to use in name lookups as well as error reporting.
* @param n The node being visited.
* @param parent The parent of the node n.
* @return whether the node is typeable or not
*/
boolean visitName(NodeTraversal t, Node n, Node parent) {
// At this stage, we need to determine whether this is a leaf
// node in an expression (which therefore needs to have a type
// assigned for it) versus some other decorative node that we
// can safely ignore. Function names, arguments (children of LP nodes) and
// variable declarations are ignored.
// TODO(user): remove this short-circuiting in favor of a
// pre order traversal of the FUNCTION, CATCH, LP and VAR nodes.
int parentNodeType = parent.getType();
if (parentNodeType == Token.FUNCTION ||
parentNodeType == Token.CATCH ||
parentNodeType == Token.LP ||
parentNodeType == Token.VAR) {
return false;
}
JSType type = n.getJSType();
if (type == null) {
type = getNativeType(UNKNOWN_TYPE);
Var var = t.getScope().getVar(
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>n.getString());
if (var != null) {
JSType varType = var.getType();
if (varType != null) {
type = varType;
}
}
}
ensureTyped(t, n, type);
return true;
}
/**
* Visits a GETPROP node.
*
* @param t The node traversal object that supplies context, such as the
* scope chain to use in name lookups as well as error reporting.
* @param n The node being visited.
* @param parent The parent of <code>n</code>
*/
private void visitGetProp(NodeTraversal t, Node n, Node parent) {
// GETPROP nodes have an assigned type on their node by the scope creator
// if this is an enum declaration. The only namespaced enum declarations
// that we allow are of the form object.name = ...;
if (n.getJSType() != null && parent.getType() == Token.ASSIGN) {
return;
}
// obj.prop or obj.method()
// Lots of types can appear on the left, a call to a void function can
// never be on the left. getPropertyType will decide what is acceptable
// and what isn't.
Node property = n.getLastChild();
Node objNode = n.getFirstChild();
JSType childType = getJSType(objNode);
// TODO(user): remove in favor of flagging every property access on
// non-object.
if (!validator.expectNotNullOrUndefined(t, n, childType,
childType + " has no properties", getNativeType(OBJECT_TYPE))) {
ensureTyped(t, n);
return;
}
checkPropertyAccess(childType, property.getString(), t, n);
ensureTyped(t, n);
}
/**
* Make sure that the access of this property is ok.
*/
private void checkPropertyAccess(JSType childType, String propName,
NodeTraversal t, Node n) {
ObjectType objectType = childType.dereference();
if (objectType != null) {
JSType propType = getJSType(n);
if ((!objectType.hasProperty(propName) ||
objectType.equals(typeRegistry.getNativeType(UNKNOWN_TYPE))) &&
propType.equals(
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>typeRegistry.getNativeType(UNKNOWN_TYPE))) {
if (objectType instanceof EnumType) {
report(t, n, INEXISTENT_ENUM_ELEMENT, propName);
} else if (!objectType.isEmptyType() &&
reportMissingProperties && !isPropertyTest(n)) {
if (!typeRegistry.canPropertyBeDefined(objectType, propName)) {
report(t, n, INEXISTENT_PROPERTY, propName,
validator.getReadableJSTypeName(n.getFirstChild(), true));
}
}
}
} else {
// TODO(nicksantos): might want to flag the access on a non object when
// it's impossible to get a property from this type.
}
}
/**
* Determines whether this node is testing for the existence of a property.
* If true, we will not emit warnings about a missing property.
*
* @param getProp The GETPROP being tested.
*/
private boolean isPropertyTest(Node getProp) {
Node parent = getProp.getParent();
switch (parent.getType()) {
case Token.CALL:
return parent.getFirstChild() != getProp &&
compiler.getCodingConvention().isPropertyTestFunction(parent);
case Token.IF:
case Token.WHILE:
case Token.DO:
case Token.FOR:
return NodeUtil.getConditionExpression(parent) == getProp;
case Token.INSTANCEOF:
case Token.TYPEOF:
return true;
case Token.AND:
case Token.HOOK:
return parent.getFirstChild() == getProp;
case Token.NOT:
return parent.getParent().getType() == Token.OR &&
parent.getParent().getFirstChild() == parent;
}
return false;
}
/**
* Visits a GETELEM node.
*
* @param t The node traversal object that supplies context, such as the
* scope chain to use in name lookups as well as error reporting.
* @param n The node being visited.
*/
private void visitGetElem(NodeTraversal t, Node n) {
Node left = n.getFirstChild();
Node right = n.getLastChild();
validator.expectIndexMatch(t, n, getJSType(left), getJSType(right));
ensureTyped(t, n);
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
/**
* Visits a VAR node.
*
* @param t The node traversal object that supplies context, such as the
* scope chain to use in name lookups as well as error reporting.
* @param n The node being visited.
*/
private void visitVar(NodeTraversal t, Node n) {
// TODO(nicksantos): Fix this so that the doc info always shows up
// on the NAME node. We probably want to wait for the parser
// merge to fix this.
JSDocInfo varInfo = n.hasOneChild() ? n.getJSDocInfo() : null;
for (Node name : n.children()) {
Node value = name.getFirstChild();
// A null var would indicate a bug in the scope creation logic.
Var var = t.getScope().getVar(name.getString());
if (value != null) {
JSType valueType = getJSType(value);
JSType nameType = var.getType();
nameType = (nameType == null) ? getNativeType(UNKNOWN_TYPE) : nameType;
JSDocInfo info = name.getJSDocInfo();
if (info == null) {
info = varInfo;
}
if (info != null && info.hasEnumParameterType()) {
// var.getType() can never be null, this would indicate a bug in the
// scope creation logic.
checkEnumInitializer(
t, value,
info.getEnumParameterType().evaluate(t.getScope(), typeRegistry));
} else if (var.isTypeInferred()) {
ensureTyped(t, name, valueType);
} else {
validator.expectCanAssignTo(
t, value, valueType, nameType, "initializing variable");
}
}
}
}
/**
* Visits a NEW node.
*/
private void visitNew(NodeTraversal t, Node n) {
Node constructor = n.getFirstChild();
FunctionType type = getFunctionType(constructor);
if (type != null && type.isConstructor()) {
visitParameterList(t, n, type);
ensureTyped(t, n, type.getInstanceType());
} else {
// TODO(user): add support for namespaced objects.
if (constructor.getType() != Token.GETPROP) {
// TODO(
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> left, leftType,
"bad left operand to bitwise operator");
validator.expectBitwiseable(t, right, rightType,
"bad right operand to bitwise operator");
break;
case Token.ASSIGN_ADD:
case Token.ADD:
break;
default:
report(t, n, UNEXPECTED_TOKEN, Node.tokenToName(op));
}
ensureTyped(t, n);
}
/**
* <p>Checks the initializer of an enum. An enum can be initialized with an
* object literal whose values must be subtypes of the declared enum element
* type, or by copying another enum.</p>
*
* <p>In the case of an enum copy, we verify that the enum element type of the
* enum used for initialization is a subtype of the enum element type of
* the enum the value is being copied in.</p>
*
* <p>Examples:</p>
* <pre>var myEnum = {FOO: ..., BAR: ...};
* var myEnum = myOtherEnum;</pre>
*
* @param value the value used for initialization of the enum
* @param primitiveType The type of each element of the enum.
*/
private void checkEnumInitializer(
NodeTraversal t, Node value, JSType primitiveType) {
if (value.getType() == Token.OBJECTLIT) {
for (Node key = value.getFirstChild();
key != null; key = key.getNext()) {
Node propValue = key.getFirstChild();
// the value's type must be assignable to the enum's primitive type
validator.expectCanAssignTo(
t, propValue, getJSType(propValue), primitiveType,
"element type must match enum's type");
}
} else if (value.getJSType() instanceof EnumType) {
// TODO(user): Remove the instanceof check in favor
// of a type.isEnumType() predicate. Currently, not all enum types are
// implemented by the EnumClass, e.g. the unknown type and the any
// type. The types need to be defined by interfaces such that an
// implementation can implement multiple types interface.
EnumType valueEnumType = (EnumType) value.getJSType();
JSType valueEnumPrimitiveType =
valueEnumType.getElementsType().getPrimitiveType();
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> validator.expectCanAssignTo(t, value, valueEnumPrimitiveType,
primitiveType, "incompatible enum element types");
} else {
// The error condition is handled in TypedScopeCreator.
}
}
/**
* This predicate is used to determine if the node represents an expression
* that is a Reference according to JavaScript definitions.
*
* @param n The node being checked.
* @return true if the sub-tree n is a reference, false otherwise.
*/
private static boolean isReference(Node n) {
switch (n.getType()) {
case Token.GETELEM:
case Token.GETPROP:
case Token.NAME:
return true;
default:
return false;
}
}
/**
* This method gets the JSType from the Node argument and verifies that it is
* present.
*/
private JSType getJSType(Node n) {
JSType jsType = n.getJSType();
if (jsType == null) {
// TODO(nicksantos): This branch indicates a compiler bug, not worthy of
// halting the compilation but we should log this and analyze to track
// down why it happens. This is not critical and will be resolved over
// time as the type checker is extended.
return getNativeType(UNKNOWN_TYPE);
} else {
return jsType;
}
}
/**
* Gets the type of the node or {@code null} if the node's type is not a
* function.
*/
private FunctionType getFunctionType(Node n) {
JSType type = getJSType(n).restrictByNotNullOrUndefined();
if (type.isUnknownType()) {
return typeRegistry.getNativeFunctionType(U2U_CONSTRUCTOR_TYPE);
} else if (type instanceof FunctionType) {
return (FunctionType) type;
} else {
return null;
}
}
// TODO(nicksantos): TypeCheck should never be attaching types to nodes.
// All types should be attached by TypeInference. This is not true today
// for legacy reasons. There are a number of places where TypeInference
// doesn't attach a type, as a signal to TypeCheck that it needs to check
// that node's type.
/**
* Ensure that the given node has a type. If it does not have one
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>,
* attach the UNKNOWN_TYPE.
*/
private void ensureTyped(NodeTraversal t, Node n) {
ensureTyped(t, n, getNativeType(UNKNOWN_TYPE));
}
private void ensureTyped(NodeTraversal t, Node n, JSTypeNative type) {
ensureTyped(t, n, getNativeType(type));
}
/**
* Enforces type casts, and ensures the node is typed.
*
* A cast in the way that we use it in JSDoc annotations never
* alters the generated code and therefore never can induce any runtime
* operation. What this means is that a 'cast' is really just a compile
* time constraint on the underlying value. In the future, we may add
* support for run-time casts for compiled tests.
*
* To ensure some shred of sanity, we enforce the notion that the
* type you are casting to may only meaningfully be a narrower type
* than the underlying declared type. We also invalidate optimizations
* on bad type casts.
*
* @param t The traversal object needed to report errors.
* @param n The node getting a type assigned to it.
* @param type The type to be assigned.
*/
private void ensureTyped(NodeTraversal t, Node n, JSType type) {
// Make sure FUNCTION nodes always get function type.
Preconditions.checkState(n.getType() != Token.FUNCTION ||
type instanceof FunctionType ||
type.isUnknownType());
JSDocInfo info = n.getJSDocInfo();
if (info != null) {
if (info.hasType()) {
JSType infoType = info.getType().evaluate(t.getScope(), typeRegistry);
validator.expectCanCast(t, n, infoType, type);
type = infoType;
}
if (info.isImplicitCast() && !inExterns) {
String propName = n.getType() == Token.GETPROP ?
n.getLastChild().getString() : "(missing)";
compiler.report(
t.makeError(n, ILLEGAL_IMPLICIT_CAST, propName));
}
}
if (n.getJSType() == null) {
n.setJSType(type);
}
}
/**
* Returns the percentage of
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> return null;
}
private Node transform(AstNode node) {
JSDocInfo jsDocInfo = handleJsDoc(node);
Node irNode = justTransform(node);
if (jsDocInfo != null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, node);
return irNode;
}
private Node transformNameAsString(Name node) {
JSDocInfo jsDocInfo = handleJsDoc(node);
Node irNode = transformDispatcher.processName(node, true);
if (jsDocInfo != null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, node);
return irNode;
}
private Node transformNumberAsString(NumberLiteral literalNode) {
JSDocInfo jsDocInfo = handleJsDoc(literalNode);
Node irNode = newStringNode(getStringValue(literalNode.getNumber()));
if (jsDocInfo != null) {
irNode.setJSDocInfo(jsDocInfo);
}
setSourceInfo(irNode, literalNode);
return irNode;
}
private static String getStringValue(double value) {
long longValue = (long) value;
// Return "1" instead of "1.0"
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
private void setSourceInfo(Node irNode, AstNode node) {
// If we have a named function, set the position to that of the name.
if (irNode.getType() == Token.FUNCTION &&
irNode.getFirstChild().getLineno() != -1) {
irNode.setLineno(irNode.getFirstChild().getLineno());
irNode.setCharno(irNode.getFirstChild().getCharno());
} else {
if (irNode.getLineno() == -1) {
// If we didn't already set the line, then set it now. This avoids
// cases like ParenthesizedExpression where we just return a previous
// node, but don't want the new node to get its parent's line number.
int lineno = node.getLineno();
irNode.setLineno(lineno);
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>transform(statementNode.getStatement()));
return node;
}
@Override
Node processName(Name nameNode) {
return processName(nameNode, false);
}
Node processName(Name nameNode, boolean asString) {
if (asString) {
return newStringNode(Token.STRING, nameNode.getIdentifier());
} else {
if (isReservedKeyword(nameNode.getIdentifier())) {
errorReporter.error(
"identifier is a reserved word",
sourceName,
nameNode.getLineno(), "", 0);
}
return newStringNode(Token.NAME, nameNode.getIdentifier());
}
}
/**
* @return Whether the
*/
private boolean isReservedKeyword(String identifier) {
return reservedKeywords != null && reservedKeywords.contains(identifier);
}
@Override
Node processNewExpression(NewExpression exprNode) {
return processFunctionCall(exprNode);
}
@Override
Node processNumberLiteral(NumberLiteral literalNode) {
return newNumberNode(literalNode.getNumber());
}
@Override
Node processObjectLiteral(ObjectLiteral literalNode) {
if (literalNode.isDestructuring()) {
reportDestructuringAssign(literalNode);
}
Node node = newNode(Token.OBJECTLIT);
for (ObjectProperty el : literalNode.getElements()) {
if (config.languageMode == LanguageMode.ECMASCRIPT3) {
if (el.isGetter()) {
reportGetter(el);
continue;
} else if (el.isSetter()) {
reportSetter(el);
continue;
}
}
Node key = transformAsString(el.getLeft());
Node value = transform(el.getRight());
if (el.isGetter()) {
key.setType(Token.GET);
Preconditions.checkState(value.getType() == Token.FUNCTION);
if (getFnParamNode(value).hasChildren()) {
reportGetterParam(el.getLeft());
}
} else if (el.isSetter()) {
key.setType(Token.SET);
Preconditions.checkState(value.getType() == Token.FUNCTION);
if (!getFnParamNode(value).hasOneChild()) {
reportSetterParam(el.getLeft());
}
}
key.addChildToFront(value);
node.
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>Block.getLineno());
}
return node;
}
@Override
Node processUnaryExpression(UnaryExpression exprNode) {
int type = transformTokenType(exprNode.getType());
Node operand = transform(exprNode.getOperand());
if (type == Token.NEG && operand.getType() == Token.NUMBER) {
operand.setDouble(-operand.getDouble());
return operand;
} else {
if (type == Token.INC || type == Token.DEC) {
if (!validAssignmentTarget(operand)) {
String msg = (type == Token.INC)
? "invalid increment target"
: "invalid decrement target";
errorReporter.error(
msg,
sourceName,
operand.getLineno(), "", 0);
}
}
Node node = newNode(type, operand);
if (exprNode.isPostfix()) {
node.putBooleanProp(Node.INCRDECR_PROP, true);
}
return node;
}
}
private boolean validAssignmentTarget(Node target) {
switch (target.getType()) {
case Token.NAME:
case Token.GETPROP:
case Token.GETELEM:
return true;
}
return false;
}
@Override
Node processVariableDeclaration(VariableDeclaration declarationNode) {
if (!config.acceptConstKeyword && declarationNode.getType() ==
com.google.javascript.jscomp.mozilla.rhino.Token.CONST) {
processIllegalToken(declarationNode);
}
Node node = newNode(Token.VAR);
for (VariableInitializer child : declarationNode.getVariables()) {
node.addChildToBack(transform(child));
}
return node;
}
@Override
Node processVariableInitializer(VariableInitializer initializerNode) {
Node node = transform(initializerNode.getTarget());
if (initializerNode.getInitializer() != null) {
node.addChildToBack(transform(initializerNode.getInitializer()));
node.setLineno(node.getLineno());
}
return node;
}
@Override
Node processWhileLoop(WhileLoop loopNode) {
return newNode(
Token.WHILE,
transform(loopNode.getCondition()),
transformBlock(loopNode.getBody()));
}
@Override
Node processWithStatement(WithStatement statementNode) {
return newNode(
Token.
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>Token.DIV:
return Token.DIV;
case com.google.javascript.jscomp.mozilla.rhino.Token.MOD:
return Token.MOD;
case com.google.javascript.jscomp.mozilla.rhino.Token.NOT:
return Token.NOT;
case com.google.javascript.jscomp.mozilla.rhino.Token.BITNOT:
return Token.BITNOT;
case com.google.javascript.jscomp.mozilla.rhino.Token.POS:
return Token.POS;
case com.google.javascript.jscomp.mozilla.rhino.Token.NEG:
return Token.NEG;
case com.google.javascript.jscomp.mozilla.rhino.Token.NEW:
return Token.NEW;
case com.google.javascript.jscomp.mozilla.rhino.Token.DELPROP:
return Token.DELPROP;
case com.google.javascript.jscomp.mozilla.rhino.Token.TYPEOF:
return Token.TYPEOF;
case com.google.javascript.jscomp.mozilla.rhino.Token.GETPROP:
return Token.GETPROP;
case com.google.javascript.jscomp.mozilla.rhino.Token.SETPROP:
return Token.SETPROP;
case com.google.javascript.jscomp.mozilla.rhino.Token.GETELEM:
return Token.GETELEM;
case com.google.javascript.jscomp.mozilla.rhino.Token.SETELEM:
return Token.SETELEM;
case com.google.javascript.jscomp.mozilla.rhino.Token.CALL:
return Token.CALL;
case com.google.javascript.jscomp.mozilla.rhino.Token.NAME:
return Token.NAME;
case com.google.javascript.jscomp.mozilla.rhino.Token.NUMBER:
return Token.NUMBER;
case com.google.javascript.jscomp.mozilla.rhino.Token.STRING:
return Token.STRING;
case com.google.javascript.jscomp.mozilla.rhino.Token.NULL:
return Token.NULL;
case com.google.javascript.jscomp.mozilla.rhino.Token.THIS:
return Token.THIS;
case com.google.javascript.jscomp.mozilla.rhino.Token.FALSE
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> create nodes and set the initial node properties.
private Node newNode(int type) {
return new Node(type).clonePropsFrom(templateNode);
}
private Node newNode(int type, Node child1) {
return new Node(type, child1).clonePropsFrom(templateNode);
}
private Node newNode(int type, Node child1, Node child2) {
return new Node(type, child1, child2).clonePropsFrom(templateNode);
}
private Node newNode(int type, Node child1, Node child2, Node child3) {
return new Node(type, child1, child2, child3).clonePropsFrom(templateNode);
}
private Node newStringNode(String value) {
return Node.newString(value).clonePropsFrom(templateNode);
}
private Node newStringNode(int type, String value) {
return Node.newString(type, value).clonePropsFrom(templateNode);
}
private Node newNumberNode(Double value) {
return Node.newNumber(value).clonePropsFrom(templateNode);
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.javascript.jscomp.CheckLevel;
/**
* Sets the level for a particular DiagnosticGroup.
* @author nicksantos@google.com (Nick Santos)
*/
public class DiagnosticGroupWarningsGuard extends WarningsGuard {
private final DiagnosticGroup group;
private final CheckLevel level;
public DiagnosticGroupWarningsGuard(
DiagnosticGroup group, CheckLevel level) {
this.group = group;
this.level = level;
}
@Override
public CheckLevel level(JSError error) {
return group.matches(error) ? level : null;
}
@Override
public boolean disables(DiagnosticGroup otherGroup) {
return !level.isOn() && group.isSubGroup(otherGroup);
}
@Override
public boolean enables(DiagnosticGroup otherGroup) {
if (level.isOn()) {
for (DiagnosticType type : otherGroup.getTypes()) {
if (group.matches(type)) {
return true;
}
}
}
return false;
}
@Override
public String toString() {
return group + "(" + level + ")";
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> return implicitPrototype.isPropertyTypeInferred(property);
}
// property does not exist
return false;
}
return p.inferred;
}
@Override
public JSType getPropertyType(String propertyName) {
Property p = properties.get(propertyName);
if (p != null) {
return p.type;
}
ObjectType implicitPrototype = getImplicitPrototype();
if (implicitPrototype != null) {
return implicitPrototype.getPropertyType(propertyName);
}
return getNativeType(JSTypeNative.UNKNOWN_TYPE);
}
@Override
public boolean isPropertyInExterns(String propertyName) {
Property p = properties.get(propertyName);
if (p != null) {
return p.inExterns;
}
ObjectType implicitPrototype = getImplicitPrototype();
if (implicitPrototype != null) {
return implicitPrototype.isPropertyInExterns(propertyName);
}
return false;
}
@Override
boolean defineProperty(String name, JSType type, boolean inferred,
boolean inExterns, Node propertyNode) {
if (hasOwnDeclaredProperty(name)) {
return false;
}
properties.put(name, new Property(type, inferred, inExterns, propertyNode));
return true;
}
@Override
public Node getPropertyNode(String propertyName) {
Property p = properties.get(propertyName);
if (p != null) {
return p.propertyNode;
}
ObjectType implicitPrototype = getImplicitPrototype();
if (implicitPrototype != null) {
return implicitPrototype.getPropertyNode(propertyName);
}
return null;
}
@Override
public JSDocInfo getOwnPropertyJSDocInfo(String propertyName) {
Property p = properties.get(propertyName);
if (p != null) {
return p.docInfo;
}
return null;
}
@Override
public void setPropertyJSDocInfo(String propertyName, JSDocInfo info,
boolean inExterns) {
if (info != null) {
if (!properties.containsKey(propertyName)) {
// If docInfo was attached, but the type of the property
// was not defined anywhere, then we consider this an explicit
// declaration of the property.
defineInferredProperty(propertyName, getPropertyType(propertyName),
inExterns, null);
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
return RecordType.isSubtype(this, (RecordType) that);
}
// Interfaces
// Find all the interfaces implemented by this class and compare each one
// to the interface instance.
ObjectType thatObj = that.toObjectType();
ObjectType thatCtor = thatObj == null ? null : thatObj.getConstructor();
if (thatCtor != null && thatCtor.isInterface()) {
Iterable<ObjectType> thisInterfaces = getCtorImplementedInterfaces();
for (ObjectType thisInterface : thisInterfaces) {
if (thisInterface.isSubtype(that)) {
return true;
}
}
}
// other prototype based objects
if (that != null) {
if (isUnknownType() || implicitPrototypeChainIsUnknown()) {
// If unsure, say 'yes', to avoid spurious warnings.
// TODO(user): resolve the prototype chain completely in all cases,
// to avoid guessing.
return true;
}
return this.isImplicitPrototype(thatObj);
}
return false;
}
private boolean implicitPrototypeChainIsUnknown() {
ObjectType p = getImplicitPrototype();
while (p != null) {
if (p.isUnknownType()) {
return true;
}
p = p.getImplicitPrototype();
}
return false;
}
private static final class Property implements Serializable {
private static final long serialVersionUID = 1L;
/**
* Property's type.
*/
private JSType type;
/**
* Whether the property's type is inferred.
*/
private final boolean inferred;
/**
* Whether the property is defined in the externs.
*/
private final boolean inExterns;
/**
* The node corresponding to this property, e.g., a GETPROP node that
* declares this property.
*/
private final Node propertyNode;
/** The JSDocInfo for this property. */
private JSDocInfo docInfo = null;
private Property(JSType type, boolean inferred, boolean inExterns, Node propertyNode) {
this.type = type;
this.inferred = inferred;
this.inExterns = inExterns;
this.propertyNode = propertyNode;
}
}
@Override
public boolean hasCachedValues() {
return super.hasCachedValues();
}
/** Whether this is a
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> built-in object. */
@Override
public boolean isNativeObjectType() {
return nativeType;
}
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
setResolvedTypeInternal(this);
ObjectType implicitPrototype = getImplicitPrototype();
if (implicitPrototype != null) {
implicitPrototypeFallback =
(ObjectType) implicitPrototype.resolve(t, scope);
}
for (Property prop : properties.values()) {
prop.type = safeResolve(prop.type, t, scope);
}
return this;
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> if the object is named.
* @return true if the object is named, false if it is anonymous
*/
public boolean hasReferenceName() {
return false;
}
@Override
public TernaryValue testForEquality(JSType that) {
// super
TernaryValue result = super.testForEquality(that);
if (result != null) {
return result;
}
// objects are comparable to everything but null/undefined
if (that.isSubtype(
getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN))) {
return UNKNOWN;
} else {
return FALSE;
}
}
/**
* Gets this object's constructor.
* @return this object's constructor or {@code null} if it is a native
* object (constructed natively v.s. by instantiation of a function)
*/
public abstract FunctionType getConstructor();
/**
* Gets the implicit prototype (a.k.a. the {@code [[Prototype]]} property).
*/
public abstract ObjectType getImplicitPrototype();
/**
* Defines a property whose type is synthesized (i.e. not inferred).
* @param propertyName the property's name
* @param type the type
* @param inExterns {@code true} if this property was defined in an externs
* file. TightenTypes assumes that any function passed to an externs
* property could be called, so setting this incorrectly could result
* in live code being removed.
* @param propertyNode the node corresponding to the declaration of property
* which might later be accessed using {@code getPropertyNode}.
*/
public final boolean defineDeclaredProperty(String propertyName,
JSType type, boolean inExterns, Node propertyNode) {
boolean result = defineProperty(propertyName, type, false, inExterns,
propertyNode);
// All property definitions go through this method
// or defineDeclaredProperty. Because the properties defined an an
// object can affect subtyping, it's slightly more efficient
// to register this after defining the property.
registry.registerPropertyOnType(propertyName, this);
return result;
}
/**
* Defines a property whose type is inferred.
* @param propertyName the property's name
* @param type the type
* @param inExterns {@code true} if this property
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> was defined in an externs
* file. TightenTypes assumes that any function passed to an externs
* property could be called, so setting this incorrectly could result
* in live code being removed.
* @param propertyNode the node corresponding to the inferred definition of
* property that might later be accessed using {@code getPropertyNode}.
*/
public final boolean defineInferredProperty(String propertyName,
JSType type, boolean inExterns, Node propertyNode) {
if (hasProperty(propertyName)) {
JSType originalType = getPropertyType(propertyName);
type = originalType == null ? type :
originalType.getLeastSupertype(type);
}
boolean result = defineProperty(propertyName, type, true, inExterns,
propertyNode);
// All property definitions go through this method
// or defineDeclaredProperty. Because the properties defined an an
// object can affect subtyping, it's slightly more efficient
// to register this after defining the property.
registry.registerPropertyOnType(propertyName, this);
return result;
}
/**
* Defines a property.<p>
*
* For clarity, callers should prefer {@link #defineDeclaredProperty} and
* {@link #defineInferredProperty}.
*
* @param propertyName the property's name
* @param type the type
* @param inferred {@code true} if this property's type is inferred
* @param inExterns {@code true} if this property was defined in an externs
* file. TightenTypes assumes that any function passed to an externs
* property could be called, so setting this incorrectly could result
* in live code being removed.
* @param propertyNode the node that represents the definition of property.
* Depending on the actual sub-type the node type might be different.
* The general idea is to have an estimate of where in the source code
* this property is defined.
* @return True if the property was registered successfully, false if this
* conflicts with a previous property type declaration.
*/
abstract boolean defineProperty(String propertyName, JSType type,
boolean inferred, boolean inExterns, Node propertyNode);
/**
* Gets the node corresponding to the definition of the specified property.
* This could be the node corresponding to declaration of the property or
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
return BooleanLiteralSet.TRUE;
}
/**
* We treat this as the unknown type if any of its implicit prototype
* properties is unknown.
*/
@Override
public boolean isUnknownType() {
// If the object is unknown now, check the supertype again,
// because it might have been resolved since the last check.
if (unknown) {
ObjectType implicitProto = getImplicitPrototype();
if (implicitProto == null ||
implicitProto.isNativeObjectType()) {
unknown = false;
} else {
unknown = implicitProto.isUnknownType();
}
}
return unknown;
}
@Override
public boolean isObject() {
return true;
}
/**
* Returns true if any cached valeus have been set for this type. If true,
* then the prototype chain should not be changed, as it might invalidate the
* cached values.
*/
public boolean hasCachedValues() {
return !unknown;
}
/**
* Clear cached values. Should be called before making changes to a prototype
* that may have been changed since creation.
*/
public void clearCachedValues() {
unknown = true;
}
/** Whether this is a built-in object. */
public boolean isNativeObjectType() {
return false;
}
/**
* A null-safe version of JSType#toObjectType.
*/
public static ObjectType cast(JSType type) {
return type == null ? null : type.toObjectType();
}
/**
* Gets the interfaces implemented by the ctor associated with this type.
* Intended to be overridden by subclasses.
*/
public Iterable<ObjectType> getCtorImplementedInterfaces() {
return ImmutableSet.of();
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
/**
* Group a set of related diagnostic types together, so that they can
* be toggled on and off as one unit.
* @author nicksantos@google.com (Nick Santos)
*/
public class DiagnosticGroup {
// The set of types represented by this group, hashed by key.
private final Set<DiagnosticType> types;
// A human-readable name for the group.
private final String name;
/**
* Create a group that matches all errors of the given types.
*/
DiagnosticGroup(String name, DiagnosticType ...types) {
this.name = name;
this.types = ImmutableSet.copyOf(Arrays.asList(types));
}
/**
* Create a group that matches all errors of the given types.
*/
public DiagnosticGroup(DiagnosticType ...types) {
this(null, types);
}
/**
* Create a diagnostic group with no name that only matches the given type.
*/
private DiagnosticGroup(DiagnosticType type) {
this.name = null;
this.types = ImmutableSet.of(type);
}
// DiagnosticGroups with only a single DiagnosticType.
private static final Map<DiagnosticType, DiagnosticGroup> singletons =
Maps.newHashMap();
/** Create a diagnostic group that matches only the given type
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>. */
static DiagnosticGroup forType(DiagnosticType type) {
if (!singletons.containsKey(type)) {
singletons.put(type, new DiagnosticGroup(type));
}
return singletons.get(type);
}
/**
* Create a composite group.
*/
public DiagnosticGroup(DiagnosticGroup ...groups) {
this(null, groups);
}
/**
* Create a composite group.
*/
public DiagnosticGroup(String name, DiagnosticGroup ...groups) {
Set<DiagnosticType> set = Sets.newHashSet();
for (DiagnosticGroup group : groups) {
set.addAll(group.types);
}
this.name = name;
this.types = ImmutableSet.copyOf(set);
}
/**
* Returns whether the given error's type matches a type
* in this group.
*/
public boolean matches(JSError error) {
return matches(error.getType());
}
/**
* Returns whether the given type matches a type in this group.
*/
public boolean matches(DiagnosticType type) {
return types.contains(type);
}
/**
* Returns whether all of the types in the given group are in this group.
*/
boolean isSubGroup(DiagnosticGroup group) {
for (DiagnosticType type : group.types) {
if (!matches(type)) {
return false;
}
}
return true;
}
/**
* Returns an iterator over all the types in this group.
*/
Collection<DiagnosticType> getTypes() {
return types;
}
public String toString() {
return name == null ? super.toString() : "DiagnosticGroup<" + name + ">";
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>) {
super(registry, registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE));
Preconditions.checkNotNull(reference);
this.reference = reference;
this.sourceName = sourceName;
this.lineno = lineno;
this.charno = charno;
}
@Override
boolean defineProperty(String propertyName, JSType type,
boolean inferred, boolean inExterns, Node propertyNode) {
if (!isResolved()) {
// If this is an unresolved object type, we need to save all its
// properties and define them when it is resolved.
if (propertyContinuations == null) {
propertyContinuations = Lists.newArrayList();
}
propertyContinuations.add(
new PropertyContinuation(
propertyName, type, inferred, inExterns, propertyNode));
return true;
} else {
return super.defineProperty(
propertyName, type, inferred, inExterns, propertyNode);
}
}
private void finishPropertyContinuations() {
ObjectType referencedObjType = getReferencedObjTypeInternal();
if (referencedObjType != null && !referencedObjType.isUnknownType()) {
if (propertyContinuations != null) {
for (PropertyContinuation c : propertyContinuations) {
c.commit(this);
}
}
}
propertyContinuations = null;
}
/** Returns the type to which this refers (which is unknown if unresolved). */
public JSType getReferencedType() {
return getReferencedTypeInternal();
}
@Override
public String getReferenceName() {
return reference;
}
@Override
public String toString() {
return reference;
}
@Override
public boolean hasReferenceName() {
return true;
}
@Override
boolean isNamedType() {
return true;
}
@Override
public boolean isNominalType() {
return true;
}
/**
* Two named types are equivalent if they are the same {@code
* ObjectType} object. This is complicated by the fact that isEquivalent
* is sometimes called before we have a chance to resolve the type
* names.
*
* @return {@code true} iff {@code that} == {@code this} or {@code that}
* is a {@link NamedType} whose reference is the same as ours,
* or {@code that} is the type we reference
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>.
*/
@Override
public boolean isEquivalentTo(JSType that) {
if (this == that) {
return true;
}
ObjectType objType = ObjectType.cast(that);
if (objType != null) {
return objType.isNominalType() &&
reference.equals(objType.getReferenceName());
}
return false;
}
@Override
public int hashCode() {
return reference.hashCode();
}
/**
* Resolve the referenced type within the enclosing scope.
*/
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> enclosing) {
// TODO(user): Investigate whether it is really necessary to keep two
// different mechanisms for resolving named types, and if so, which order
// makes more sense. Now, resolution via registry is first in order to
// avoid triggering the warnings built into the resolution via properties.
boolean resolved = resolveViaRegistry(t, enclosing);
if (detectImplicitPrototypeCycle()) {
handleTypeCycle(t);
}
if (resolved) {
super.resolveInternal(t, enclosing);
finishPropertyContinuations();
return registry.isLastGeneration() ?
getReferencedType() : this;
}
resolveViaProperties(t, enclosing);
if (detectImplicitPrototypeCycle()) {
handleTypeCycle(t);
}
super.resolveInternal(t, enclosing);
if (isResolved()) {
finishPropertyContinuations();
}
return registry.isLastGeneration() ?
getReferencedType() : this;
}
/**
* Resolves a named type by looking it up in the registry.
* @return True if we resolved successfully.
*/
private boolean resolveViaRegistry(
ErrorReporter t, StaticScope<JSType> enclosing) {
JSType type = registry.getType(reference);
if (type != null) {
setReferencedAndResolvedType(type, t, enclosing);
return true;
}
return false;
}
/**
* Resolves a named type by looking up its first component in the scope, and
* subsequent components as properties. The scope must have been fully
* parsed and a symbol table constructed.
*/
private void resolveViaProperties(ErrorReporter t,
StaticScope<JSType> enclosing) {
JSType value = lookupViaProperties
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>(t, enclosing);
// last component of the chain
if ((value instanceof FunctionType) &&
(value.isConstructor() || value.isInterface())) {
FunctionType functionType = (FunctionType) value;
setReferencedAndResolvedType(
functionType.getInstanceType(), t, enclosing);
} else if (value instanceof EnumType) {
setReferencedAndResolvedType(
((EnumType) value).getElementsType(), t, enclosing);
} else {
// We've been running into issues where people forward-declare
// non-named types. (This is legitimate...our dependency management
// code doubles as our forward-declaration code.)
//
// So if the type does resolve to an actual value, but it's not named,
// then don't respect the forward declaration.
handleUnresolvedType(t, value == null || value.isUnknownType());
}
}
/**
* Resolves a type by looking up its first component in the scope, and
* subsequent components as properties. The scope must have been fully
* parsed and a symbol table constructed.
* @return The type of the symbol, or null if the type could not be found.
*/
private JSType lookupViaProperties( ErrorReporter t,
StaticScope<JSType> enclosing) {
String[] componentNames = reference.split("\\.", -1);
if (componentNames[0].length() == 0) {
return null;
}
StaticSlot<JSType> slot = enclosing.getSlot(componentNames[0]);
if (slot == null) {
return null;
}
// If the first component has a type of 'Unknown', then any type
// names using it should be regarded as silently 'Unknown' rather than be
// noisy about it.
JSType slotType = slot.getType();
if (slotType == null || slotType.isAllType() || slotType.isNoType()) {
return null;
}
JSType value = getTypedefType(t, slot, componentNames[0]);
if (value == null) {
return null;
}
// resolving component by component
for (int i = 1; i < componentNames.length; i++) {
ObjectType parentClass = ObjectType.cast(value);
if (parentClass == null) {
return null
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>;
}
if (componentNames[i].length() == 0) {
return null;
}
value = parentClass.getPropertyType(componentNames[i]);
}
return value;
}
private void setReferencedAndResolvedType(JSType type, ErrorReporter t,
StaticScope<JSType> enclosing) {
if (validator != null) {
validator.apply(type);
}
setReferencedType(type);
checkEnumElementCycle(t);
setResolvedTypeInternal(getReferencedType());
}
private void handleTypeCycle(ErrorReporter t) {
setReferencedType(
registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE));
t.warning("Cycle detected in inheritance chain of type " + reference,
sourceName, lineno, null, charno);
setResolvedTypeInternal(getReferencedType());
}
private void checkEnumElementCycle(ErrorReporter t) {
JSType referencedType = getReferencedType();
if (referencedType instanceof EnumElementType &&
((EnumElementType) referencedType).getPrimitiveType() == this) {
handleTypeCycle(t);
}
}
// Warns about this type being unresolved iff it's not a forward-declared
// type name.
private void handleUnresolvedType(
ErrorReporter t, boolean ignoreForwardReferencedTypes) {
if (registry.isLastGeneration()) {
boolean isForwardDeclared =
ignoreForwardReferencedTypes &&
registry.isForwardDeclaredType(reference);
if (!isForwardDeclared && registry.isLastGeneration()) {
t.warning("Bad type annotation. Unknown type " + reference,
sourceName, lineno, null, charno);
} else {
setReferencedType(
registry.getNativeObjectType(
JSTypeNative.NO_RESOLVED_TYPE));
if (registry.isLastGeneration() && validator != null) {
validator.apply(getReferencedType());
}
}
setResolvedTypeInternal(getReferencedType());
} else {
setResolvedTypeInternal(this);
}
}
JSType getTypedefType(ErrorReporter t, StaticSlot<JSType> slot, String name) {
JSType type = slot.getType();
if (type != null) {
return type;
}
handleUnresolvedType(t, true);
return null;
}
@Override
public boolean
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> setValidator(Predicate<JSType> validator) {
// If the type is already resolved, we can validate it now. If
// the type has not been resolved yet, we need to wait till its
// resolved before we can validate it.
if (this.isResolved()) {
return super.setValidator(validator);
} else {
this.validator = validator;
return true;
}
}
/** Store enough information to define a property at a later time. */
private static final class PropertyContinuation {
private final String propertyName;
private final JSType type;
private final boolean inferred;
private final boolean inExterns;
private final Node propertyNode;
private PropertyContinuation(
String propertyName,
JSType type,
boolean inferred,
boolean inExterns,
Node propertyNode) {
this.propertyName = propertyName;
this.type = type;
this.inferred = inferred;
this.inExterns = inExterns;
this.propertyNode = propertyNode;
}
void commit(ObjectType target) {
target.defineProperty(
propertyName, type, inferred, inExterns, propertyNode);
}
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> */
boolean skipRenaming;
/** Set of nodes for this field that need renaming. */
Set<Node> renameNodes = Sets.newHashSet();
/**
* Map from node to the highest type in the prototype chain containing the
* field for that node. In the case of a union, the type is the highest type
* of one of the types in the union.
*/
final Map<Node, T> rootTypes = Maps.newHashMap();
Property(String name) {
this.name = name;
}
/** Returns the types on which this field is referenced. */
UnionFind<T> getTypes() {
if (types == null) {
types = new StandardUnionFind<T>();
}
return types;
}
/**
* Record that this property is referenced from this type.
* @return true if the type was recorded for this property, else false,
* which would happen if the type was invalidating.
*/
boolean addType(T type, T top, T relatedType) {
checkState(!skipRenaming, "Attempt to record skipped property: %s", name);
if (typeSystem.isInvalidatingType(top)) {
invalidate();
return false;
} else {
if (typeSystem.isTypeToSkip(top)) {
addTypeToSkip(top);
}
if (relatedType == null) {
getTypes().add(top);
} else {
getTypes().union(top, relatedType);
}
typeSystem.recordInterfaces(type, top, this);
return true;
}
}
/** Records the given type as one to skip for this property. */
void addTypeToSkip(T type) {
for (T skipType : typeSystem.getTypesToSkipForType(type)) {
typesToSkip.add(skipType);
getTypes().union(skipType, type);
}
}
/** Invalidates any types related to invalid types. */
void expandTypesToSkip() {
// If we are not going to rename any properties, then we do not need to
// update the list of invalid types, as they are all invalid.
if (shouldRename()) {
int count = 0;
while (true) {
// It should usually only take one time through this do-while.
checkState(++
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>count < 10, "Stuck in loop expanding types to skip.");
// Make sure that the representative type for each type to skip is
// marked as being skipped.
Set<T> rootTypesToSkip = Sets.newHashSet();
for (T subType : typesToSkip) {
rootTypesToSkip.add(types.find(subType));
}
typesToSkip.addAll(rootTypesToSkip);
Set<T> newTypesToSkip = Sets.newHashSet();
Set<T> allTypes = types.elements();
int originalTypesSize = allTypes.size();
for (T subType : allTypes) {
if (!typesToSkip.contains(subType)
&& typesToSkip.contains(types.find(subType))) {
newTypesToSkip.add(subType);
}
}
for (T newType : newTypesToSkip) {
addTypeToSkip(newType);
}
// If there were not any new types added, we are done here.
if (types.elements().size() == originalTypesSize) {
break;
}
}
}
}
/** Returns true if any instance of this property should be renamed. */
boolean shouldRename() {
return !skipRenaming && types != null
&& types.allEquivalenceClasses().size() > 1;
}
/**
* Returns true if this property should be renamed on this type.
* expandTypesToSkip() should be called before this, if anything has been
* added to the typesToSkip list.
*/
boolean shouldRename(T type) {
return !skipRenaming && !typesToSkip.contains(type);
}
/**
* Invalidates a field from renaming. Used for field references on an
* object with unknown type.
*/
boolean invalidate() {
boolean changed = !skipRenaming;
skipRenaming = true;
types = null;
return changed;
}
/**
* Schedule the node to potentially be renamed.
* @param node the node to rename
* @param type the highest type in the prototype chain for which the
* property is defined
* @return True if type was accepted without invalidation or if the property
* was already invalidated. False if this property was invalidated this
* time.
*/
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
boolean scheduleRenaming(Node node, T type) {
if (!skipRenaming) {
if (typeSystem.isInvalidatingType(type)) {
invalidate();
return false;
}
renameNodes.add(node);
rootTypes.put(node, type);
}
return true;
}
}
private Map<String, Property> properties = Maps.newHashMap();
static DisambiguateProperties<JSType> forJSTypeSystem(
AbstractCompiler compiler) {
return new DisambiguateProperties<JSType>(
compiler, new JSTypeSystem(compiler));
}
static DisambiguateProperties<ConcreteType> forConcreteTypeSystem(
AbstractCompiler compiler, TightenTypes tt) {
return new DisambiguateProperties<ConcreteType>(
compiler, new ConcreteTypeSystem(tt, compiler.getCodingConvention()));
}
/**
* This constructor should only be called by one of the helper functions
* above for either the JSType system, or the concrete type system.
*/
private DisambiguateProperties(AbstractCompiler compiler,
TypeSystem<T> typeSystem) {
this.compiler = compiler;
this.typeSystem = typeSystem;
this.showInvalidationWarnings = compiler.getErrorLevel(
JSError.make("", 0, 0, Warnings.INVALIDATION)) != CheckLevel.OFF;
}
public void process(Node externs, Node root) {
for (TypeMismatch mis : compiler.getTypeValidator().getMismatches()) {
addInvalidatingType(mis.typeA);
addInvalidatingType(mis.typeB);
}
StaticScope<T> scope = typeSystem.getRootScope();
NodeTraversal.traverse(compiler, externs, new FindExternProperties());
NodeTraversal.traverse(compiler, root, new FindRenameableProperties());
renameProperties();
}
/**
* Invalidates the given type, so that no properties on it will be renamed.
*/
private void addInvalidatingType(JSType type) {
type = type.restrictByNotNullOrUndefined();
if (type instanceof UnionType) {
for (JSType alt : ((UnionType) type).getAlternates()) {
addInvalidatingType(alt);
}
return;
}
typeSystem.addInvalidatingType(type);
ObjectType objType
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> = ObjectType.cast(type);
if (objType != null && objType.getImplicitPrototype() != null) {
typeSystem.addInvalidatingType(objType.getImplicitPrototype());
}
}
/** Returns the property for the given name, creating it if necessary. */
protected Property getProperty(String name) {
if (!properties.containsKey(name)) {
properties.put(name, new Property(name));
}
return properties.get(name);
}
/** Public for testing. */
T getTypeWithProperty(String field, T type) {
return typeSystem.getTypeWithProperty(field, type);
}
/** Tracks the current type system scope while traversing. */
private abstract class AbstractScopingCallback implements ScopedCallback {
protected final Stack<StaticScope<T>> scopes =
new Stack<StaticScope<T>>();
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
return true;
}
public void enterScope(NodeTraversal t) {
if (t.inGlobalScope()) {
scopes.push(typeSystem.getRootScope());
} else {
scopes.push(typeSystem.getFunctionScope(t.getScopeRoot()));
}
}
public void exitScope(NodeTraversal t) {
scopes.pop();
}
/** Returns the current scope at this point in the file. */
protected StaticScope<T> getScope() {
return scopes.peek();
}
}
/**
* Finds all properties defined in the externs file and sets them as
* ineligible for renaming from the type on which they are defined.
*/
private class FindExternProperties extends AbstractScopingCallback {
@Override public void visit(NodeTraversal t, Node n, Node parent) {
// TODO(johnlenz): Support object-literal property definitions.
if (n.getType() == Token.GETPROP) {
String field = n.getLastChild().getString();
T type = typeSystem.getType(getScope(), n.getFirstChild(), field);
Property prop = getProperty(field);
if (typeSystem.isInvalidatingType(type)) {
prop.invalidate();
} else {
prop.addTypeToSkip(type);
// If this is a prototype property, then we want to skip assignments
// to the instance type as
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> well. These assignments are not usually
// seen in the extern code itself, so we must handle them here.
if ((type = typeSystem.getInstanceFromPrototype(type)) != null) {
prop.getTypes().add(type);
prop.typesToSkip.add(type);
}
}
}
}
}
/**
* Traverses the tree, building a map from field names to Nodes for all
* fields that can be renamed.
*/
private class FindRenameableProperties extends AbstractScopingCallback {
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.GETPROP) {
handleGetProp(t, n);
} else if (n.getType() == Token.OBJECTLIT) {
handleObjectLit(t, n);
}
}
/**
* Processes a GETPROP node.
*/
private void handleGetProp(NodeTraversal t, Node n) {
String name = n.getLastChild().getString();
T type = typeSystem.getType(getScope(), n.getFirstChild(), name);
Property prop = getProperty(name);
if (!prop.scheduleRenaming(n.getLastChild(),
processProperty(t, prop, type, null))) {
if (showInvalidationWarnings) {
compiler.report(JSError.make(
t.getSourceName(), n, Warnings.INVALIDATION, name,
(type == null ? "null" : type.toString()), n.toString()));
}
}
}
/**
* Processes a OBJECTLIT node.
*/
private void handleObjectLit(NodeTraversal t, Node n) {
Node child = n.getFirstChild();
while (child != null) {
// Maybe STRING, GET, SET
// We should never see a mix of numbers and strings.
String name = child.getString();
T type = typeSystem.getType(getScope(), n, name);
Property prop = getProperty(name);
if (!prop.scheduleRenaming(child,
processProperty(t, prop, type, null))) {
if (showInvalidationWarnings) {
compiler.report(JSError.make(
t.getSourceName(), child, Warnings.INVALIDATION, name,
(type == null ? "null" : type.toString()), n.
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>toString()));
}
}
child = child.getNext();
}
}
/**
* Processes a property, adding it to the list of properties to rename.
* @return a representative type for the property reference, which will be
* the highest type on the prototype chain of the provided type. In the
* case of a union type, it will be the highest type on the prototype
* chain of one of the members of the union.
*/
private T processProperty(
NodeTraversal t, Property prop, T type, T relatedType) {
type = typeSystem.restrictByNotNullOrUndefined(type);
if (prop.skipRenaming || typeSystem.isInvalidatingType(type)) {
return null;
}
Iterable<T> alternatives = typeSystem.getTypeAlternatives(type);
if (alternatives != null) {
T firstType = relatedType;
for (T subType : alternatives) {
T lastType = processProperty(t, prop, subType, firstType);
if (lastType != null) {
firstType = firstType == null ? lastType : firstType;
}
}
return firstType;
} else {
T topType = typeSystem.getTypeWithProperty(prop.name, type);
if (typeSystem.isInvalidatingType(topType)) {
return null;
}
prop.addType(type, topType, relatedType);
return topType;
}
}
}
/** Renames all properties with references on more than one type. */
void renameProperties() {
int propsRenamed = 0, propsSkipped = 0, instancesRenamed = 0,
instancesSkipped = 0, singleTypeProps = 0;
for (Property prop : properties.values()) {
if (prop.shouldRename()) {
Map<T, String> propNames = buildPropNames(prop.getTypes(), prop.name);
++propsRenamed;
prop.expandTypesToSkip();
UnionFind<T> types = prop.getTypes();
for (Node node : prop.renameNodes) {
T rootType = prop.rootTypes.get(node);
if (prop.shouldRename(rootType)) {
String newName = propNames.get(rootType);
node.setString(newName);
compiler
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>.reportCodeChange();
++instancesRenamed;
} else {
++instancesSkipped;
}
}
} else {
if (prop.skipRenaming) {
++propsSkipped;
} else {
++singleTypeProps;
}
}
}
logger.info("Renamed " + instancesRenamed + " instances of "
+ propsRenamed + " properties.");
logger.info("Skipped renaming " + instancesSkipped + " invalidated "
+ "properties, " + propsSkipped + " instances of properties "
+ "that were skipped for specific types and " + singleTypeProps
+ " properties that were referenced from only one type.");
}
/**
* Chooses a name to use for renaming in each equivalence class and maps
* each type in that class to it.
*/
private Map<T, String> buildPropNames(UnionFind<T> types, String name) {
Map<T, String> names = Maps.newHashMap();
for (Set<T> set : types.allEquivalenceClasses()) {
checkState(!set.isEmpty());
String typeName = null;
for (T type : set) {
if (typeName == null || type.toString().compareTo(typeName) < 0) {
typeName = type.toString();
}
}
String newName;
if ("{...}".equals(typeName)) {
newName = name;
} else {
newName = typeName.replaceAll("[^\\w$]", "_") + "$" + name;
}
for (T type : set) {
names.put(type, newName);
}
}
return names;
}
/** Returns a map from field name to types for which it will be renamed. */
Multimap<String, Collection<T>> getRenamedTypesForTesting() {
Multimap<String, Collection<T>> ret = HashMultimap.create();
for (Map.Entry<String, Property> entry: properties.entrySet()) {
Property prop = entry.getValue();
if (!prop.skipRenaming) {
for (Collection<T> c : prop.getTypes().allEquivalenceClasses()) {
if (!c.isEmpty() && !prop.typesToSkip.contains(c.iterator().next())) {
ret.put(entry.getKey(), c);
}
}
}
}
return ret
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>;
}
/** Interface for providing the type information needed by this pass. */
private interface TypeSystem<T> {
// TODO(user): add a getUniqueName(T type) method that is guaranteed
// to be unique, performant and human-readable.
/** Returns the top-most scope used by the type system (if any). */
StaticScope<T> getRootScope();
/** Returns the new scope started at the given function node. */
StaticScope<T> getFunctionScope(Node node);
/**
* Returns the type of the given node.
* @param prop Only types with this property need to be returned. In general
* with type tightening, this will require no special processing, but in
* the case of an unknown JSType, we might need to add in the native
* types since we don't track them, but only if they have the given
* property.
*/
T getType(StaticScope<T> scope, Node node, String prop);
/**
* Returns true if a field reference on this type will invalidiate all
* references to that field as candidates for renaming. This is true if the
* type is unknown or all-inclusive, as variables with such a type could be
* references to any object.
*/
boolean isInvalidatingType(T type);
/**
* Informs the given type system that a type is invalidating due to a type
* mismatch found during type checking.
*/
void addInvalidatingType(JSType type);
/**
* Returns a set of types that should be skipped given the given type.
* This is necessary for interfaces when using JSTypes, as all super
* interfaces must also be skipped.
*/
ImmutableSet<T> getTypesToSkipForType(T type);
/**
* Determines whether the given type is one whose properties should not be
* considered for renaming.
*/
boolean isTypeToSkip(T type);
/** Remove null and undefined from the options in the given type. */
T restrictByNotNullOrUndefined(T type);
/**
* Returns the alternatives if this is a type that represents multiple
* types, and null if not. Union and interface types can correspond to
* multiple other types.
*/
Iterable<T> getTypeAlternatives(T type);
/**
* Returns the type in the chain from the
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> given type that contains the given
* field or null if it is not found anywhere.
*/
T getTypeWithProperty(String field, T type);
/**
* Returns the type of the instance of which this is the prototype or null
* if this is not a function prototype.
*/
T getInstanceFromPrototype(T type);
/**
* Records that this property could be referenced from any interface that
* this type, or any type in its superclass chain, implements.
*/
void recordInterfaces(T type, T relatedType,
DisambiguateProperties<T>.Property p);
}
/** Implementation of TypeSystem using JSTypes. */
private static class JSTypeSystem implements TypeSystem<JSType> {
private final Set<JSType> invalidatingTypes;
private JSTypeRegistry registry;
public JSTypeSystem(AbstractCompiler compiler) {
registry = compiler.getTypeRegistry();
invalidatingTypes = Sets.newHashSet(
registry.getNativeType(JSTypeNative.ALL_TYPE),
registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE),
registry.getNativeType(JSTypeNative.NO_TYPE),
registry.getNativeType(JSTypeNative.FUNCTION_PROTOTYPE),
registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE),
registry.getNativeType(JSTypeNative.OBJECT_PROTOTYPE),
registry.getNativeType(JSTypeNative.TOP_LEVEL_PROTOTYPE),
registry.getNativeType(JSTypeNative.UNKNOWN_TYPE));
}
@Override public void addInvalidatingType(JSType type) {
checkState(!type.isUnionType());
invalidatingTypes.add(type);
}
@Override public StaticScope<JSType> getRootScope() { return null; }
@Override public StaticScope<JSType> getFunctionScope(Node node) {
return null;
}
@Override public JSType getType(
StaticScope<JSType> scope, Node node, String prop) {
if (node.getJSType() == null) {
return registry.getNativeType(JSTypeNative.UNKNOWN_TYPE);
}
return node.getJSType();
}
@Override public boolean isInvalidatingType(JSType type) {
if (type == null || invalidatingTypes.contains(type) ||
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
type.isUnknownType() /* unresolved types */) {
return true;
}
ObjectType objType = ObjectType.cast(type);
return objType != null && !objType.hasReferenceName();
}
@Override public ImmutableSet<JSType> getTypesToSkipForType(JSType type) {
type = type.restrictByNotNullOrUndefined();
if (type instanceof UnionType) {
Set<JSType> types = Sets.newHashSet(type);
for (JSType alt : ((UnionType) type).getAlternates()) {
types.addAll(getTypesToSkipForTypeNonUnion(type));
}
return ImmutableSet.copyOf(types);
}
return ImmutableSet.copyOf(getTypesToSkipForTypeNonUnion(type));
}
private Set<JSType> getTypesToSkipForTypeNonUnion(JSType type) {
Set<JSType> types = Sets.newHashSet();
JSType skipType = type;
while (skipType != null) {
types.add(skipType);
ObjectType objSkipType = skipType.toObjectType();
if (objSkipType != null) {
skipType = objSkipType.getImplicitPrototype();
} else {
break;
}
}
return types;
}
@Override public boolean isTypeToSkip(JSType type) {
return type.isEnumType() || (type.autoboxesTo() != null);
}
@Override public JSType restrictByNotNullOrUndefined(JSType type) {
return type.restrictByNotNullOrUndefined();
}
@Override public Iterable<JSType> getTypeAlternatives(JSType type) {
if (type.isUnionType()) {
return ((UnionType) type).getAlternates();
} else {
ObjectType objType = type.toObjectType();
if (objType != null &&
objType.getConstructor() != null &&
objType.getConstructor().isInterface()) {
List<JSType> list = Lists.newArrayList();
for (FunctionType impl
: registry.getDirectImplementors(objType)) {
list.add(impl.getInstanceType());
}
return list;
} else {
return null;
}
}
}
@Override public ObjectType getTypeWithProperty(String field, JSType type) {
if (!(type instanceof ObjectType)) {
if (
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>type.autoboxesTo() != null) {
type = type.autoboxesTo();
} else {
return null;
}
}
// Ignore the prototype itself at all times.
if ("prototype".equals(field)) {
return null;
}
// We look up the prototype chain to find the highest place (if any) that
// this appears. This will make references to overriden properties look
// like references to the initial property, so they are renamed alike.
ObjectType foundType = null;
ObjectType objType = ObjectType.cast(type);
while (objType != null && objType.getImplicitPrototype() != objType) {
if (objType.hasOwnProperty(field)) {
foundType = objType;
}
objType = objType.getImplicitPrototype();
}
// If the property does not exist on the referenced type but the original
// type is an object type, see if any subtype has the property.
if (foundType == null) {
ObjectType maybeType = ObjectType.cast(
registry.getGreatestSubtypeWithProperty(type, field));
// getGreatestSubtypeWithProperty does not guarantee that the property
// is defined on the returned type, it just indicates that it might be,
// so we have to double check.
if (maybeType != null && maybeType.hasOwnProperty(field)) {
foundType = maybeType;
}
}
return foundType;
}
@Override public JSType getInstanceFromPrototype(JSType type) {
if (type.isFunctionPrototypeType()) {
FunctionPrototypeType prototype = (FunctionPrototypeType) type;
FunctionType owner = prototype.getOwnerFunction();
if (owner.isConstructor() || owner.isInterface()) {
return ((FunctionPrototypeType) type).getOwnerFunction()
.getInstanceType();
}
}
return null;
}
@Override
public void recordInterfaces(JSType type, JSType relatedType,
DisambiguateProperties<JSType>.Property p) {
ObjectType objType = ObjectType.cast(type);
if (objType != null) {
FunctionType constructor;
if (objType instanceof FunctionType) {
constructor = (FunctionType) objType;
} else if (objType instanceof FunctionPrototypeType) {
constructor = ((FunctionPrototypeType) objType).getOwnerFunction();
} else {
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> constructor = objType.getConstructor();
}
while (constructor != null) {
for (ObjectType itype : constructor.getImplementedInterfaces()) {
JSType top = getTypeWithProperty(p.name, itype);
if (top != null) {
p.addType(itype, top, relatedType);
} else {
recordInterfaces(itype, relatedType, p);
}
// If this interface invalidated this property, return now.
if (p.skipRenaming) return;
}
if (constructor.isInterface() || constructor.isConstructor()) {
constructor = constructor.getSuperClassConstructor();
} else {
constructor = null;
}
}
}
}
}
/** Implementation of TypeSystem using concrete types. */
private static class ConcreteTypeSystem implements TypeSystem<ConcreteType> {
private final TightenTypes tt;
private int nextUniqueId;
private CodingConvention codingConvention;
private final Set<JSType> invalidatingTypes = Sets.newHashSet();
// An array of native types that are not tracked by type tightening, and
// thus need to be added in if an unknown type is encountered.
private static final JSTypeNative [] nativeTypes = new JSTypeNative[] {
JSTypeNative.BOOLEAN_OBJECT_TYPE,
JSTypeNative.NUMBER_OBJECT_TYPE,
JSTypeNative.STRING_OBJECT_TYPE
};
public ConcreteTypeSystem(TightenTypes tt, CodingConvention convention) {
this.tt = tt;
this.codingConvention = convention;
}
@Override public void addInvalidatingType(JSType type) {
checkState(!type.isUnionType());
invalidatingTypes.add(type);
}
@Override public StaticScope<ConcreteType> getRootScope() {
return tt.getTopScope();
}
@Override public StaticScope<ConcreteType> getFunctionScope(Node decl) {
ConcreteFunctionType func = tt.getConcreteFunction(decl);
return (func != null) ?
func.getScope() : (StaticScope<ConcreteType>) null;
}
@Override
public ConcreteType getType(
StaticScope<ConcreteType> scope, Node node, String prop) {
if (scope != null) {
ConcreteType c = tt.inferConcreteType(
(TightenTypes.ConcreteScope) scope
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>, node);
return maybeAddAutoboxes(c, node, prop);
} else {
return null;
}
}
/**
* Add concrete types for autoboxing types if necessary. The concrete type
* system does not track native types, like string, so add them if they are
* present in the JSType for the node.
*/
private ConcreteType maybeAddAutoboxes(
ConcreteType cType, Node node, String prop) {
JSType jsType = node.getJSType();
if (jsType == null) {
return cType;
} else if (jsType.isUnknownType()) {
for (JSTypeNative nativeType : nativeTypes) {
ConcreteType concrete = tt.getConcreteInstance(
tt.getTypeRegistry().getNativeObjectType(nativeType));
if (concrete != null && !concrete.getPropertyType(prop).isNone()) {
cType = cType.unionWith(concrete);
}
}
return cType;
}
return maybeAddAutoboxes(cType, jsType, prop);
}
private ConcreteType maybeAddAutoboxes(
ConcreteType cType, JSType jsType, String prop) {
jsType = jsType.restrictByNotNullOrUndefined();
if (jsType instanceof UnionType) {
for (JSType alt : ((UnionType) jsType).getAlternates()) {
return maybeAddAutoboxes(cType, alt, prop);
}
}
if (jsType.autoboxesTo() != null) {
JSType autoboxed = jsType.autoboxesTo();
return cType.unionWith(tt.getConcreteInstance((ObjectType) autoboxed));
} else if (jsType.unboxesTo() != null) {
return cType.unionWith(tt.getConcreteInstance((ObjectType) jsType));
}
return cType;
}
@Override public boolean isInvalidatingType(ConcreteType type) {
// We will disallow types on functions so that 'prototype' is not renamed.
// TODO(user): Support properties on functions as well.
return (type == null) || type.isAll() || type.isFunction()
|| (type.isInstance()
&& invalidatingTypes.contains(type.toInstance().instanceType));
}
@Override
public ImmutableSet<ConcreteType> getTypesToSkip
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>ForType(ConcreteType type) {
return ImmutableSet.of(type);
}
@Override public boolean isTypeToSkip(ConcreteType type) {
// Skip anonymous object literals and enum types.
return type.isInstance()
&& !(type.toInstance().isFunctionPrototype()
|| type.toInstance().instanceType.isInstanceType());
}
@Override
public ConcreteType restrictByNotNullOrUndefined(ConcreteType type) {
// These are not represented in concrete types.
return type;
}
@Override
public Iterable<ConcreteType> getTypeAlternatives(ConcreteType type) {
if (type.isUnion()) {
return ((ConcreteUnionType) type).getAlternatives();
} else {
return null;
}
}
@Override public ConcreteType getTypeWithProperty(String field,
ConcreteType type) {
if (type.isInstance()) {
ConcreteInstanceType instanceType = (ConcreteInstanceType) type;
return instanceType.getInstanceTypeWithProperty(field);
} else if (type.isFunction()) {
if ("prototype".equals(field)
|| codingConvention.isSuperClassReference(field)) {
return type;
}
} else if (type.isNone()) {
// If the receiver is none, then this code is never reached. We will
// return a new fake type to ensure that this access is renamed
// differently from any other, so it can be easily removed.
return new ConcreteUniqueType(++nextUniqueId);
} else if (type.isUnion()) {
// If only one has the property, return that.
for (ConcreteType t : ((ConcreteUnionType) type).getAlternatives()) {
ConcreteType ret = getTypeWithProperty(field, t);
if (ret != null) {
return ret;
}
}
}
return null;
}
@Override public ConcreteType getInstanceFromPrototype(ConcreteType type) {
if (type.isInstance()) {
ConcreteInstanceType instanceType = (ConcreteInstanceType) type;
if (instanceType.isFunctionPrototype()) {
return instanceType.getConstructorType().getInstanceType();
}
}
return null;
}
@Override
public void recordInterfaces(ConcreteType type, ConcreteType relatedType,
DisambiguateProperties<ConcreteType>.Property p) {
// No need to record interfaces when
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> }
super.add(n, context);
}
private String getTypeAnnotation(Node node) {
JSType type = node.getJSType();
if (type instanceof FunctionType) {
return getFunctionAnnotation(node);
} else if (type != null && !type.isUnknownType()
&& !type.isEmptyType() && !type.isVoidType() &&
!type.isFunctionPrototypeType()) {
return "/** @type {" + node.getJSType() + "} */\n";
} else {
return "";
}
}
/**
* @param fnNode A node for a function for which to generate a type annotation
*/
private String getFunctionAnnotation(Node fnNode) {
Preconditions.checkState(fnNode.getType() == Token.FUNCTION);
StringBuilder sb = new StringBuilder("/**\n");
JSType type = fnNode.getJSType();
if (type == null || type.isUnknownType()) {
return "";
}
FunctionType funType = (FunctionType) fnNode.getJSType();
// We need to use the child nodes of the function as the nodes for the
// parameters of the function type do not have the real parameter names.
// FUNCTION
// NAME
// LP
// NAME param1
// NAME param2
if (fnNode != null) {
Node paramNode = NodeUtil.getFnParameters(fnNode).getFirstChild();
// Param types
for (Node n : funType.getParameters()) {
// Bail out if the paramNode is not there.
if (paramNode == null) {
break;
}
sb.append(" * @param {" + getParameterNodeJSDocType(n) + "} ");
sb.append(paramNode.getString());
sb.append("\n");
paramNode = paramNode.getNext();
}
}
// Return type
JSType retType = funType.getReturnType();
if (retType != null && !retType.isUnknownType() && !retType.isEmptyType()) {
sb.append(" * @return {" + retType + "}\n");
}
// Constructor/interface
if (funType.isConstructor() || funType.isInterface()) {
FunctionType superConstructor = funType.getSuperClassConstructor();
if (superConstructor != null)
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>Constructor() {
return referencedType.isConstructor();
}
@Override
public boolean isNominalType() {
return referencedType.isNominalType();
}
@Override
public boolean isInstanceType() {
return referencedType.isInstanceType();
}
@Override
public boolean isInterface() {
return referencedType.isInterface();
}
@Override
public boolean isOrdinaryFunction() {
return referencedType.isOrdinaryFunction();
}
@Override
public TernaryValue testForEquality(JSType that) {
return referencedType.testForEquality(that);
}
@Override
public boolean isSubtype(JSType that) {
return referencedType.isSubtype(that);
}
@Override
public Iterable<ObjectType> getCtorImplementedInterfaces() {
return referencedObjType == null ? Collections.<ObjectType>emptyList() :
referencedObjType.getCtorImplementedInterfaces();
}
@Override
public boolean canAssignTo(JSType that) {
return referencedType.canAssignTo(that);
}
@Override
public boolean isEquivalentTo(JSType that) {
if (this == that) {
return true;
}
return referencedType.isEquivalentTo(that);
}
@Override
public int hashCode() {
return referencedType.hashCode();
}
@Override
public String toString() {
return referencedType.toString();
}
@Override
public ObjectType getImplicitPrototype() {
return referencedObjType == null ? null :
referencedObjType.getImplicitPrototype();
}
@Override
boolean defineProperty(String propertyName, JSType type,
boolean inferred, boolean inExterns, Node propertyNode) {
return referencedObjType == null ? true :
referencedObjType.defineProperty(
propertyName, type, inferred, inExterns, propertyNode);
}
@Override
public boolean isPropertyTypeDeclared(String propertyName) {
return referencedObjType == null ? false :
referencedObjType.isPropertyTypeDeclared(propertyName);
}
@Override
public Node getPropertyNode(String propertyName) {
return referencedObjType == null ? null :
referencedObjType.getPropertyNode(propertyName);
}
@Override
public boolean isPropertyTypeInferred(String propertyName) {
return referencedObjType == null ? false :
referencedObjType.isPropertyTypeInferred(propertyName);
}
@Override
public boolean isPropertyInExterns(
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> {
// If a.b is not a constructor, then treat this as a method
// of whatever type is on "a".
return normalizeClassType(lValue.getFirstChild().getJSType());
}
} else {
// We have an assignment of the form "a = ...", so pull the
// type off the "a".
return normalizeClassType(lValue.getJSType());
}
}
} else if (NodeUtil.isFunctionDeclaration(n) ||
parent.getType() == Token.NAME) {
return normalizeClassType(n.getJSType());
}
return null;
}
/**
* Normalize the type of a constructor, its instance, and its prototype
* all down to the same type (the instance type).
*/
private JSType normalizeClassType(JSType type) {
if (type == null || type.isUnknownType()) {
return type;
} else if (type.isConstructor()) {
return ((FunctionType) type).getInstanceType();
} else if (type.isFunctionPrototypeType()) {
FunctionType owner = ((FunctionPrototypeType) type).getOwnerFunction();
if (owner.isConstructor()) {
return owner.getInstanceType();
}
}
return type;
}
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
return true;
}
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.NAME:
checkNameDeprecation(t, n, parent);
checkNameVisibility(t, n, parent);
break;
case Token.GETPROP:
checkPropertyDeprecation(t, n, parent);
checkPropertyVisibility(t, n, parent);
checkConstantProperty(t, n);
break;
case Token.NEW:
checkConstructorDeprecation(t, n, parent);
break;
}
}
/**
* Checks the given NEW node to ensure that access restrictions are obeyed.
*/
private void checkConstructorDeprecation(NodeTraversal t, Node n,
Node parent) {
JSType type = n.getJSType();
if (type != null) {
String deprecationInfo = getTypeDeprecationInfo(type);
if (deprecationInfo != null &&
shouldEmitDeprecationWarning(t, n, parent)) {
if
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> (!deprecationInfo.isEmpty()) {
compiler.report(
t.makeError(n, DEPRECATED_CLASS_REASON,
type.toString(), deprecationInfo));
} else {
compiler.report(
t.makeError(n, DEPRECATED_CLASS, type.toString()));
}
}
}
}
/**
* Checks the given NAME node to ensure that access restrictions are obeyed.
*/
private void checkNameDeprecation(NodeTraversal t, Node n, Node parent) {
// Don't bother checking definitions or constructors.
if (parent.getType() == Token.FUNCTION || parent.getType() == Token.VAR ||
parent.getType() == Token.NEW) {
return;
}
Scope.Var var = t.getScope().getVar(n.getString());
JSDocInfo docInfo = var == null ? null : var.getJSDocInfo();
if (docInfo != null && docInfo.isDeprecated() &&
shouldEmitDeprecationWarning(t, n, parent)) {
if (docInfo.getDeprecationReason() != null) {
compiler.report(
t.makeError(n, DEPRECATED_NAME_REASON, n.getString(),
docInfo.getDeprecationReason()));
} else {
compiler.report(
t.makeError(n, DEPRECATED_NAME, n.getString()));
}
}
}
/**
* Checks the given GETPROP node to ensure that access restrictions are
* obeyed.
*/
private void checkPropertyDeprecation(NodeTraversal t, Node n, Node parent) {
// Don't bother checking constructors.
if (parent.getType() == Token.NEW) {
return;
}
ObjectType objectType =
ObjectType.cast(dereference(n.getFirstChild().getJSType()));
String propertyName = n.getLastChild().getString();
if (objectType != null) {
String deprecationInfo
= getPropertyDeprecationInfo(objectType, propertyName);
if (deprecationInfo != null &&
shouldEmitDeprecationWarning(t, n, parent)) {
if (!deprecationInfo.isEmpty()) {
compiler.report(
t.makeError(n, DEPRECATED_PROP_REASON, propertyName,
validator.getReadableJSTypeName(n.getFirstChild(), true),
deprecationInfo));
} else {
compiler.report(
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>) ||
// Case #2
(getTypeDeprecationInfo(t.getScope().getTypeOfThis()) != null) ||
// Case #3
(scopeRootParent != null && scopeRootParent.getType() == Token.ASSIGN &&
getTypeDeprecationInfo(
getClassOfMethod(scopeRoot, scopeRootParent)) != null);
}
/**
* Returns whether this is a function node annotated as deprecated.
*/
private static boolean isDeprecatedFunction(Node n, Node parent) {
if (n.getType() == Token.FUNCTION) {
JSType type = n.getJSType();
if (type != null) {
return getTypeDeprecationInfo(type) != null;
}
}
return false;
}
/**
* Returns the deprecation reason for the type if it is marked
* as being deprecated. Returns empty string if the type is deprecated
* but no reason was given. Returns null if the type is not deprecated.
*/
private static String getTypeDeprecationInfo(JSType type) {
if (type == null) {
return null;
}
JSDocInfo info = type.getJSDocInfo();
if (info != null && info.isDeprecated()) {
if (info.getDeprecationReason() != null) {
return info.getDeprecationReason();
}
return "";
}
ObjectType objType = ObjectType.cast(type);
if (objType != null) {
ObjectType implicitProto = objType.getImplicitPrototype();
if (implicitProto != null) {
return getTypeDeprecationInfo(implicitProto);
}
}
return null;
}
/**
* Returns the deprecation reason for the property if it is marked
* as being deprecated. Returns empty string if the property is deprecated
* but no reason was given. Returns null if the property is not deprecated.
*/
private static String getPropertyDeprecationInfo(ObjectType type,
String prop) {
JSDocInfo info = type.getOwnPropertyJSDocInfo(prop);
if (info != null && info.isDeprecated()) {
if (info.getDeprecationReason() != null) {
return info.getDeprecationReason();
}
return "";
}
ObjectType implicitProto = type.getImplicitPrototype();
if (implicitProto != null) {
return getPropertyDeprecationInfo(implicitProto, prop);
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> }
return null;
}
/**
* Dereference a type, autoboxing it and filtering out null.
*/
private static JSType dereference(JSType type) {
return type == null ? null : type.dereference();
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>JSTypeRegistry registry) {
this.registry = registry;
}
/**
* Add parameters of the given type to the end of the param list.
* @return False if this is called after optional params are added.
*/
public boolean addRequiredParams(JSType ...types) {
if (hasOptionalOrVarArgs()) {
return false;
}
for (JSType type : types) {
newParameter(type);
}
return true;
}
/**
* Add optional parameters of the given type to the end of the param list.
* @param types Types for each optional parameter. The builder will make them
* undefineable.
* @return False if this is called after var args are added.
*/
public boolean addOptionalParams(JSType ...types) {
if (hasVarArgs()) {
return false;
}
for (JSType type : types) {
newParameter(registry.createOptionalType(type)).setOptionalArg(true);
}
return true;
}
/**
* Add variable arguments to the end of the parameter list.
* @return False if this is called after var args are added.
*/
public boolean addVarArgs(JSType type) {
if (hasVarArgs()) {
return false;
}
// There are two types of variable argument functions:
// 1) Programmer-defined var args
// 2) Native bottom types that can accept any argument.
// For the first one, "undefined" is a valid value for all arguments.
// For the second, we do not want to cast it up to undefined.
if (!type.isEmptyType()) {
type = registry.createOptionalType(type);
}
newParameter(type).setVarArgs(true);
return true;
}
/**
* Copies the parameter specification from the given node.
*/
public Node newParameterFromNode(Node n) {
Node newParam = newParameter(n.getJSType());
newParam.setVarArgs(n.isVarArgs());
newParam.setOptionalArg(n.isOptionalArg());
return newParam;
}
// Add a parameter to the list with the given type.
private Node newParameter(JSType type) {
Node paramNode = Node.newString(Token.NAME, "");
paramNode
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>.setJSType(type);
root.addChildToBack(paramNode);
return paramNode;
}
public Node build() {
return root;
}
private boolean hasOptionalOrVarArgs() {
Node lastChild = root.getLastChild();
return lastChild != null &&
(lastChild.isOptionalArg() || lastChild.isVarArgs());
}
public boolean hasVarArgs() {
Node lastChild = root.getLastChild();
return lastChild != null && lastChild.isVarArgs();
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>Node callNode);
/**
* Returns true if passed a string referring to the superclass. The string
* will usually be from the string node at the right of a GETPROP, e.g.
* this.superClass_.
*/
public boolean isSuperClassReference(String propertyName);
/**
* Convenience method for determining provided dependencies amongst different
* js scripts.
*/
public String extractClassNameIfProvide(Node node, Node parent);
/**
* Convenience method for determining required dependencies amongst different
* js scripts.
*/
public String extractClassNameIfRequire(Node node, Node parent);
/**
* Function name used when exporting properties.
* Signature: fn(object, publicName, symbol).
* @return function name.
*/
public String getExportPropertyFunction();
/**
* Function name used when exporting symbols.
* Signature: fn(publicPath, object).
* @return function name.
*/
public String getExportSymbolFunction();
/**
* Checks if the given CALL node is forward-declaring any types,
* and returns the name of the types if it is.
*/
public List<String> identifyTypeDeclarationCall(Node n);
/**
* Checks if the given ASSIGN node is a typedef, and returns the
* name of the type if it is.
*/
public String identifyTypeDefAssign(Node n);
/**
* In many JS libraries, the function that produces inheritance also
* adds properties to the superclass and/or subclass.
*/
public void applySubclassRelationship(FunctionType parentCtor,
FunctionType childCtor, SubclassType type);
/**
* Function name for abstract methods. An abstract method can be assigned to
* an interface method instead of an function expression in order to avoid
* linter warnings produced by assigning a function without a return value
* where a return value is expected.
* @return function name.
*/
public String getAbstractMethodName();
/**
* Checks if the given method defines a singleton getter, and if it does,
* returns the name of the class with the singleton getter. By default, always
* returns null. Meant to be overridden by subclasses.
*
* @param callNode A CALL node.
*/
public String getSingletonGetterClassName(Node callNode);
/**
* In many JS libraries, the function that adds a singleton getter to a class
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> * adds properties to the class.
*/
public void applySingletonGetter(FunctionType functionType,
FunctionType getterType, ObjectType objectType);
public DelegateRelationship getDelegateRelationship(Node callNode);
/**
* In many JS libraries, the function that creates a delegate relationship
* also adds properties to the delegator and delegate base.
*/
public void applyDelegateRelationship(
ObjectType delegateSuperclass, ObjectType delegateBase,
ObjectType delegator, FunctionType delegateProxy,
FunctionType findDelegate);
/**
* @return the name of the delegate superclass.
*/
public String getDelegateSuperclassName();
/**
* Defines the delegate proxy prototype properties. Their types depend on
* properties of the delegate base methods.
*
* @param delegateProxyPrototypes List of delegate proxy prototypes.
*/
public void defineDelegateProxyPrototypeProperties(
JSTypeRegistry registry, Scope scope,
List<ObjectType> delegateProxyPrototypes);
/**
* Gets the name of the global object.
*/
public String getGlobalObject();
/**
* Whether this CALL function is testing for the existence of a property.
*/
public boolean isPropertyTestFunction(Node call);
/**
* Checks if the given method performs a object literal cast, and if it does,
* returns information on the cast. By default, always returns null. Meant
* to be overridden by subclasses.
*
* @param t The node traversal.
* @param callNode A CALL node.
*/
public ObjectLiteralCast getObjectLiteralCast(NodeTraversal t,
Node callNode);
/**
* Returns the set of AssertionFunction.
*/
public Collection<AssertionFunctionSpec> getAssertionFunctions();
static enum SubclassType {
INHERITS,
MIXIN
}
static class SubclassRelationship {
final SubclassType type;
final String subclassName;
final String superclassName;
SubclassRelationship(SubclassType type,
Node subclassNode, Node superclassNode) {
this.type = type;
this.subclassName = subclassNode.getQualifiedName();
this.superclassName = superclassNode.getQualifiedName();
}
}
/**
* Delegates provides a mechanism and structure for identifying where classes
* can call out to optional code to augment their functionality. The optional
* code is isolated from the base code through the use of a subclass in the
* optional code derived
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> // For example: "return foo;"
append(" ");
}
append(newcode);
}
void appendOp(String op, boolean binOp) {
append(op);
}
void addOp(String op, boolean binOp) {
maybeEndStatement();
char first = op.charAt(0);
char prev = getLastChar();
if ((first == '+' || first == '-') && prev == first) {
// This is not pretty printing. This is to prevent misparsing of
// things like "x + ++y" or "x++ + ++y"
append(" ");
} else if (Character.isLetter(first) &&
isWordChar(prev)) {
// Make sure there is a space after e.g. instanceof , typeof
append(" ");
} else if (prev == '-' && first == '>') {
// Make sure that we don't emit -->
append(" ");
}
// Allow formating around the operator.
appendOp(op, binOp);
// Line breaking after an operator is always safe. Line breaking before an
// operator on the other hand is not. We only line break after a bin op
// because it looks strange.
if (binOp) {
maybeCutLine();
}
}
void addNumber(double x) {
// This is not pretty printing. This is to prevent misparsing of x- -4 as
// x--4 (which is a syntax error).
char prev = getLastChar();
if (x < 0 && prev == '-') {
add(" ");
}
if ((long) x == x) {
long value = (long) x;
long mantissa = value;
int exp = 0;
if (Math.abs(x) >= 100) {
while (mantissa / 10 * Math.pow(10, exp + 1) == value) {
mantissa /= 10;
exp++;
}
}
if (exp > 2) {
add(Long.toString(mantissa) + "E" + Integer.toString(exp));
} else {
add(Long.toString(value));
}
} else {
add(String.valueOf(x));
}
}
static boolean isWordChar(char ch) {
return (ch ==
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> {
boolean canAssign = true;
for (JSType t : alternates) {
if (t.isUnknownType()) {
return true;
}
canAssign &= t.canAssignTo(that);
}
return canAssign;
}
@Override
public boolean canBeCalled() {
for (JSType t : alternates) {
if (!t.canBeCalled()) {
return false;
}
}
return true;
}
@Override
public JSType restrictByNotNullOrUndefined() {
UnionTypeBuilder restricted = new UnionTypeBuilder(registry);
for (JSType t : alternates) {
restricted.addAlternate(t.restrictByNotNullOrUndefined());
}
return restricted.build();
}
@Override
public TernaryValue testForEquality(JSType that) {
TernaryValue result = null;
for (JSType t : alternates) {
TernaryValue test = t.testForEquality(that);
if (result == null) {
result = test;
} else if (!result.equals(test)) {
return UNKNOWN;
}
}
return result;
}
/**
* This predicate determines whether objects of this type can have the
* {@code null} value, and therefore can appear in contexts where
* {@code null} is expected.
*
* @return {@code true} for everything but {@code Number} and
* {@code Boolean} types.
*/
@Override
public boolean isNullable() {
for (JSType t : alternates) {
if (t.isNullable()) {
return true;
}
}
return false;
}
@Override
public boolean isUnknownType() {
for (JSType t : alternates) {
if (t.isUnknownType()) {
return true;
}
}
return false;
}
@Override
public JSType getLeastSupertype(JSType that) {
if (!that.isUnknownType() && !that.isUnionType()) {
for (JSType alternate : alternates) {
if (!alternate.isUnknownType() && that.isSubtype(alternate)) {
return this;
}
}
}
return getLeastSupertype(this, that);
}
JSType meet(JSType that) {
UnionTypeBuilder builder
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> = new UnionTypeBuilder(registry);
for (JSType alternate : alternates) {
if (alternate.isSubtype(that)) {
builder.addAlternate(alternate);
}
}
if (that instanceof UnionType) {
for (JSType otherAlternate : ((UnionType) that).alternates) {
if (otherAlternate.isSubtype(this)) {
builder.addAlternate(otherAlternate);
}
}
} else if (that.isSubtype(this)) {
builder.addAlternate(that);
}
JSType result = builder.build();
if (!result.isNoType()) {
return result;
} else if (this.isObject() && that.isObject()) {
return getNativeType(JSTypeNative.NO_OBJECT_TYPE);
} else {
return getNativeType(JSTypeNative.NO_TYPE);
}
}
/**
* Two union types are equal if they have the same number of alternates
* and all alternates are equal.
*/
@Override
public boolean isEquivalentTo(JSType object) {
if (object instanceof UnionType) {
UnionType that = (UnionType) object;
if (alternates.size() != that.alternates.size()) {
return false;
}
for (JSType alternate : that.alternates) {
if (!hasAlternate(alternate)) {
return false;
}
}
return true;
} else {
return false;
}
}
private boolean hasAlternate(JSType type) {
for (JSType alternate : alternates) {
if (alternate.isEquivalentTo(type)) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
return this.hashcode;
}
@Override
public boolean isUnionType() {
return true;
}
@Override
public boolean isObject() {
for (JSType alternate : alternates) {
if (!alternate.isObject()) {
return false;
}
}
return true;
}
/**
* A {@link UnionType} contains a given type (alternate) iff the member
* vector contains it.
*
* @param alternate The alternate which might be in this union.
*
* @return {@code true} if the alternate is in the union
*/
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
public boolean contains(JSType type) {
for (JSType alt : alternates) {
if (alt.isEquivalentTo(type)) {
return true;
}
}
return false;
}
/**
* Returns a more restricted union type than {@code this} one, in which all
* subtypes of {@code type} have been removed.<p>
*
* Examples:
* <ul>
* <li>{@code (number,string)} restricted by {@code number} is
* {@code string}</li>
* <li>{@code (null, EvalError, URIError)} restricted by
* {@code Error} is {@code null}</li>
* </ul>
*
* @param type the supertype of the types to remove from this union type
*/
public JSType getRestrictedUnion(JSType type) {
UnionTypeBuilder restricted = new UnionTypeBuilder(registry);
for (JSType t : alternates) {
if (t.isUnknownType() || !t.isSubtype(type)) {
restricted.addAlternate(t);
}
}
return restricted.build();
}
@Override public String toString() {
StringBuilder result = new StringBuilder();
boolean firstAlternate = true;
result.append("(");
SortedSet<JSType> sorted = new TreeSet<JSType>(ALPHA);
sorted.addAll(alternates);
for (JSType t : sorted) {
if (!firstAlternate) {
result.append("|");
}
result.append(t.toString());
firstAlternate = false;
}
result.append(")");
return result.toString();
}
@Override
public boolean isSubtype(JSType that) {
// unknown
if (that.isUnknownType()) {
return true;
}
// all type
if (that.isAllType()) {
return true;
}
for (JSType element : alternates) {
if (!element.isSubtype(that)) {
return false;
}
}
return true;
}
@Override
public JSType getRestrictedTypeGivenToBooleanOutcome(boolean outcome) {
// gather elements after restriction
UnionTypeBuilder restricted = new UnionTypeBuilder(registry);
for (JSType element : alternates) {
restricted.addAlternate(
element.getRestricted
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>TypeNative.NO_TYPE);
}
@Override
public boolean hasProperty(String propertyName) {
// has all properties, since it is any object
return true;
}
@Override
boolean defineProperty(String propertyName, JSType type,
boolean inferred, boolean inExterns, Node propertyNode) {
// nothing, all properties are defined
return true;
}
@Override
public JSDocInfo getOwnPropertyJSDocInfo(String propertyName) {
return null;
}
@Override
public void setPropertyJSDocInfo(String propertyName, JSDocInfo info,
boolean inExterns) {
// Do nothing, specific properties do not have JSDocInfo.
}
@Override
public boolean isPropertyTypeInferred(String propertyName) {
return false;
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseNoObjectType();
}
@Override
public String toString() {
return "NoObject";
}
@Override
public FunctionType getConstructor() {
return null;
}
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
return this;
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
int lineNumber,
String lineSource,
int columnNumber)
{
return new EcmaError(error, message, sourceName,
lineNumber, lineSource, columnNumber);
}
public static EcmaError typeError(String message)
{
return constructError("TypeError", message);
}
public static EcmaError typeError0(String messageId)
{
String msg = getMessage0(messageId);
return typeError(msg);
}
public static EcmaError typeError1(String messageId, String arg1)
{
String msg = getMessage1(messageId, arg1);
return typeError(msg);
}
public static EcmaError typeError2(String messageId, String arg1,
String arg2)
{
String msg = getMessage2(messageId, arg1, arg2);
return typeError(msg);
}
public static EcmaError typeError3(String messageId, String arg1,
String arg2, String arg3)
{
String msg = getMessage3(messageId, arg1, arg2, arg3);
return typeError(msg);
}
public static RuntimeException undefReadError(Object object, Object id)
{
String idStr = (id == null) ? "null" : id.toString();
return typeError2("msg.undef.prop.read", toString(object), idStr);
}
public static RuntimeException undefCallError(Object object, Object id)
{
String idStr = (id == null) ? "null" : id.toString();
return typeError2("msg.undef.method.call", toString(object), idStr);
}
public static RuntimeException undefWriteError(Object object,
Object id,
Object value)
{
String idStr = (id == null) ? "null" : id.toString();
String valueStr = toString(value);
return typeError3("msg.undef.prop.write", toString(object), idStr,
valueStr);
}
public static RuntimeException notFunctionError(Object value)
{
return notFunctionError(value, value);
}
public static RuntimeException notFunctionError(Object value,
Object messageHelper)
{
// XXX Use value for better error reporting
String msg = (messageHelper == null)
? "null" : messageHelper.toString();
return
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> typeError2("msg.isnt.function", msg,
value == null ? "null" : value.getClass().getName());
}
static int lastIndexResult(Context cx)
{
return cx.scratchIndex;
}
public static void storeUint32Result(Context cx, long value)
{
if ((value >>> 32) != 0)
throw new IllegalArgumentException();
cx.scratchUint32 = value;
}
public static long lastUint32Result(Context cx)
{
long value = cx.scratchUint32;
if ((value >>> 32) != 0)
throw new IllegalStateException();
return value;
}
static String makeUrlForGeneratedScript
(boolean isEval, String masterScriptUrl, int masterScriptLine)
{
if (isEval) {
return masterScriptUrl+'#'+masterScriptLine+"(eval)";
} else {
return masterScriptUrl+'#'+masterScriptLine+"(Function)";
}
}
static boolean isGeneratedScript(String sourceUrl) {
// ALERT: this may clash with a valid URL containing (eval) or
// (Function)
return sourceUrl.indexOf("(eval)") >= 0
|| sourceUrl.indexOf("(Function)") >= 0;
}
public static final Object[] emptyArgs = new Object[0];
public static final String[] emptyStrings = new String[0];
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>);
}
@Override
public boolean isPrivate(String name) {
return false;
}
@Override
public SubclassRelationship getClassesDefinedByCall(Node callNode) {
return null;
}
@Override
public boolean isSuperClassReference(String propertyName) {
return false;
}
@Override
public String extractClassNameIfProvide(Node node, Node parent) {
String message = "only implemented in GoogleCodingConvention";
throw new UnsupportedOperationException(message);
}
@Override
public String extractClassNameIfRequire(Node node, Node parent) {
String message = "only implemented in GoogleCodingConvention";
throw new UnsupportedOperationException(message);
}
@Override
public String getExportPropertyFunction() {
return null;
}
@Override
public String getExportSymbolFunction() {
return null;
}
@Override
public List<String> identifyTypeDeclarationCall(Node n) {
return null;
}
@Override
public String identifyTypeDefAssign(Node n) {
return null;
}
@Override
public void applySubclassRelationship(FunctionType parentCtor,
FunctionType childCtor, SubclassType type) {
// do nothing
}
@Override
public String getAbstractMethodName() {
return null;
}
@Override
public String getSingletonGetterClassName(Node callNode) {
return null;
}
@Override
public void applySingletonGetter(FunctionType functionType,
FunctionType getterType, ObjectType objectType) {
// do nothing.
}
@Override
public DelegateRelationship getDelegateRelationship(Node callNode) {
return null;
}
@Override
public void applyDelegateRelationship(
ObjectType delegateSuperclass, ObjectType delegateBase,
ObjectType delegator, FunctionType delegateProxy,
FunctionType findDelegate) {
// do nothing.
}
@Override
public String getDelegateSuperclassName() {
return null;
}
@Override
public void defineDelegateProxyPrototypeProperties(
JSTypeRegistry registry, Scope scope,
List<ObjectType> delegateProxyPrototypes) {
// do nothing.
}
@Override
public String getGlobalObject() {
return "window";
}
@Override
public boolean isPropertyTestFunction(Node call) {
return false;
}
@Override
public ObjectLiteralCast getObjectLiteralCast(NodeTraversal t,
Node callNode) {
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>, n2)) {
connect(n1, edge, n2);
}
}
/**
* Gets a node from the graph given a value. New nodes are created if that
* value has not been assigned a graph node. Values equality are compared
* using <code>Object.equals</code>.
*
* @param value The node's value.
* @return The corresponding node in the graph.
*/
public abstract GraphNode<N, E> createNode(N value);
/** Gets an immutable list of all nodes. */
public abstract Collection<GraphNode<N, E>> getNodes();
/** Gets an immutable list of all edges. */
public abstract List<GraphEdge<N, E>> getEdges();
/**
* Gets the degree of a node.
*
* @param value The node's value.
* @return The degree of the node.
*/
public abstract int getNodeDegree(N value);
public int getWeight(N value) {
return getNodeDegree(value);
}
/**
* Gets the neighboring nodes.
*
* @param value The node's value.
* @return A list of neighboring nodes.
*/
public abstract List<GraphNode<N, E>> getNeighborNodes(N value);
public abstract Iterator<GraphNode<N, E>> getNeighborNodesIterator(N value);
/**
* Retrieves an edge from the graph.
*
* @param n1 Node one.
* @param n2 Node two.
* @return The list of edges between those two values in the graph.
*/
public abstract List<GraphEdge<N, E>> getEdges(N n1, N n2);
/**
* Retrieves any edge from the graph.
*
* @param n1 Node one.
* @param n2 Node two.
* @return The first edges between those two values in the graph. null if
* there are none.
*/
public abstract GraphEdge<N, E> getFirstEdge(N n1, N n2);
/**
* Checks whether the node exists in the graph ({@link #createNode(Object)}
* has been called with that value).
*
* @param n Node.
* @return <code>true</code> if it exist.
*/
public final boolean hasNode(
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>AnnotationStack == null) {
edgeAnnotationStack = Lists.newLinkedList();
}
pushAnnotations(edgeAnnotationStack, getEdges());
}
/**
* Restores edges' annotation values to state before last
* {@link #pushEdgeAnnotations()}.
*/
public final void popEdgeAnnotations() {
Preconditions.checkNotNull(edgeAnnotationStack,
"Popping edge annotations without pushing.");
popAnnotations(edgeAnnotationStack);
}
/**
* A generic edge.
*
* @param <N> Value type that the graph node stores.
* @param <E> Value type that the graph edge stores.
*/
public interface GraphEdge<N, E> extends Annotatable {
/**
* Retrieves the edge's value.
*
* @return The value.
*/
E getValue();
GraphNode<N, E> getNodeA();
GraphNode<N, E> getNodeB();
}
/**
* A simple implementation of SubGraph that calculates adjacency by iterating
* over a node's neighbors.
*/
class SimpleSubGraph<N, E> implements SubGraph<N, E> {
private Graph<N, E> graph;
private List<GraphNode<N, E>> nodes = Lists.newArrayList();
SimpleSubGraph(Graph<N, E> graph) {
this.graph = graph;
}
public boolean isIndependentOf(N value) {
GraphNode<N, E> node = graph.getNode(value);
for (GraphNode<N, E> n : nodes) {
if (graph.getNeighborNodes(n.getValue()).contains(node)) {
return false;
}
}
return true;
}
public void addNode(N value) {
nodes.add(graph.getNodeOrFail(value));
}
}
/**
* Pushes a new list on stack and stores nodes annotations in the new list.
* Clears objects' annotations as well.
*/
private static void pushAnnotations(
Deque<GraphAnnotationState> stack,
Collection<? extends Annotatable> haveAnnotations) {
stack.push(new GraphAnnotationState(haveAnnotations.size()));
for (Annotatable h : haveAnnotations) {
stack.peek().add(new AnnotationState(h, h.getAnnotation()));
h.setAnnotation(null);
}
}
/**
*
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> {
// Only report error when there are some line number informations.
// There are synthetic nodes with no line number informations, nodes
// introduce by other passes (although not likely since this pass should
// be executed early) or some rhino bug.
if (n.getLineno() != -1 &&
// Allow spurious semi-colons and spurious breaks.
n.getType() != Token.EMPTY && n.getType() != Token.BREAK) {
compiler.report(t.makeError(n, level, UNREACHABLE_CODE));
// From now on, we are going to assume the user fixed the error and not
// give more warning related to code section reachable from this node.
new GraphReachability<Node, ControlFlowGraph.Branch>(
t.getControlFlowGraph()).recompute(n);
// Saves time by not traversing children.
return false;
}
}
return true;
}
private void initScope(ControlFlowGraph<Node> controlFlowGraph) {
new GraphReachability<Node, ControlFlowGraph.Branch>(
controlFlowGraph, new ReachablePredicate()).compute(
controlFlowGraph.getEntry().getValue());
}
@Override
public void exitScope(NodeTraversal t) {
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
}
private final class ReachablePredicate implements
Predicate<EdgeTuple<Node, ControlFlowGraph.Branch>> {
@Override
public boolean apply(EdgeTuple<Node, Branch> input) {
Branch branch = input.edge;
if (!branch.isConditional()) {
return true;
}
Node predecessor = input.sourceNode;
Node condition = NodeUtil.getConditionExpression(predecessor);
// TODO(user): Handle more complicated expression like true == true,
// etc....
if (condition != null) {
TernaryValue val = NodeUtil.getImpureBooleanValue(condition);
if (val != TernaryValue.UNKNOWN) {
return val.toBoolean(true) == (branch == Branch.ON_TRUE);
}
}
return true;
}
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> {@link #FALSE} only if all replacements of {@link #UNKNOWN} in this
* expression yield the same result. Therefore, the ternary logic coincides
* with typical boolean logic if the {@link #UNKNOWN} value is not
* present in an expression.</p>
*
* @see <a href="http://en.wikipedia.org/wiki/Ternary_logic">Ternary Logic</a>
*/
public enum TernaryValue {
/**
* {@code false}
*/
FALSE {
@Override
public TernaryValue and(TernaryValue that) {
return FALSE;
}
@Override
public TernaryValue not() {
return TRUE;
}
@Override
public TernaryValue or(TernaryValue that) {
return that;
}
@Override
public TernaryValue xor(TernaryValue that) {
return that;
}
@Override
public boolean toBoolean(boolean unknown) {
return false;
}
@Override
public String toString() {
return "false";
}
},
/**
* {@code true}
*/
TRUE {
@Override
public TernaryValue and(TernaryValue that) {
return that;
}
@Override
public TernaryValue not() {
return FALSE;
}
@Override
public TernaryValue or(TernaryValue that) {
return TRUE;
}
@Override
public TernaryValue xor(TernaryValue that) {
return that.not();
}
@Override
public boolean toBoolean(boolean unknown) {
return true;
}
@Override
public String toString() {
return "true";
}
},
/**
* {@code unknown}, it represents lack of knowledge about whether this value
* is {@code true} or {@code false}.
*/
UNKNOWN {
@Override
public TernaryValue and(TernaryValue that) {
return (FALSE.equals(that)) ? FALSE : UNKNOWN;
}
@Override
public TernaryValue not() {
return UNKNOWN;
}
@Override
public TernaryValue or(TernaryValue that) {
return (TRUE.equals(that)) ? TRUE : UNKNOWN;
}
@Override
public TernaryValue xor(TernaryValue that) {
return UNKNOWN;
}
@Override
public boolean to
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>Boolean(boolean unknown) {
return unknown;
}
@Override
public String toString() {
return "unknown";
}
};
/**
* Gets the {@code and} of {@code this} and {@code that}.
*/
public abstract TernaryValue and(TernaryValue that);
/**
* Gets the {@code not} of {@code this}.
*/
public abstract TernaryValue not();
/**
* Gets the {@code or} of {@code this} and {@code that}.
*/
public abstract TernaryValue or(TernaryValue that);
/**
* Gets the {@code xor} of {@code this} and {@code that}.
*/
public abstract TernaryValue xor(TernaryValue that);
/**
* Converts {@code this} ternary value to boolean. The {@code #TRUE} and
* {@code #FALSE} values are simply converted to {@code true} and
* {@code false} respectively, whilst the {@link #UNKNOWN} is converted
* to the specified {@code unknown} value.
*
* @param unknown the boolean value to which the {@link #UNKNOWN} value is
* converted
* @return <pre>return
* this == TRUE ? true :
* this == FALSE ? false :
* unknown</pre>
*/
public abstract boolean toBoolean(boolean unknown);
/**
* Gets the TernaryValue for the given boolean.
*/
public static TernaryValue forBoolean(boolean val) {
return val ? TernaryValue.TRUE : TernaryValue.FALSE;
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>/*
* Copyright 2004 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.javascript.jscomp.CheckLevel;
import com.google.javascript.rhino.Node;
import javax.annotation.Nullable;
/**
* Compile error description
*
*/
public class JSError {
/** A type of the error */
private final DiagnosticType type;
/** Description of the error */
public final String description;
/** Name of the source */
public final String sourceName;
/** Node where the warning occurred. */
final Node node;
/** Line number of the source */
public final int lineNumber;
/** Level */
public final CheckLevel level;
// character number
private final int charno;
//
// JSError.make - static factory methods for creating JSError objects
//
// The general form of the arguments is
//
// [source location] [level] DiagnosticType [argument ...]
//
// This order echos a typical command line diagnostic. Source location
// arguments are arranged to be sources of information in the order
// file-line-column.
//
// If the level is not given, it is taken from the level of the
// DiagnosticType.
/**
* Creates a JSError with no source information
*
* @param type The DiagnosticType
* @param arguments Arguments to be incorporated into the message
*/
public static JSError make(DiagnosticType type, String... arguments) {
return new JSError(null, null, -1, -1, type, null, arguments);
}
/**
* Creates a JSError at a given source location
*
* @
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>param sourceName The source file name
* @param lineno Line number with source file, or -1 if unknown
* @param charno Column number within line, or -1 for whole line.
* @param type The DiagnosticType
* @param arguments Arguments to be incorporated into the message
*/
public static JSError make(String sourceName, int lineno, int charno,
DiagnosticType type, String... arguments) {
return new JSError(sourceName, null, lineno, charno, type, null, arguments);
}
/**
* Creates a JSError at a given source location
*
* @param sourceName The source file name
* @param lineno Line number with source file, or -1 if unknown
* @param charno Column number within line, or -1 for whole line.
* @param type The DiagnosticType
* @param arguments Arguments to be incorporated into the message
*/
public static JSError make(String sourceName, int lineno, int charno,
CheckLevel level, DiagnosticType type, String... arguments) {
return new JSError(
sourceName, null, lineno, charno, type, level, arguments);
}
/**
* Creates a JSError from a file and Node position.
*
* @param sourceName The source file name
* @param n Determines the line and char position within the source file name
* @param type The DiagnosticType
* @param arguments Arguments to be incorporated into the message
*/
public static JSError make(String sourceName, Node n,
DiagnosticType type, String... arguments) {
return new JSError(sourceName, n, type, arguments);
}
/**
* Creates a JSError from a file and Node position.
*
* @param sourceName The source file name
* @param n Determines the line and char position within the source file name
* @param type The DiagnosticType
* @param arguments Arguments to be incorporated into the message
*/
public static JSError make(String sourceName, Node n, CheckLevel level,
DiagnosticType type, String... arguments) {
return new JSError(sourceName, n, n.getLineno(), n.getCharno(), type, level,
arguments);
}
//
// JSError constructors
//
/**
* Creates a JSError at a CheckLevel for a source
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> file location.
* Private to avoid any entanglement with code outside of the compiler.
*/
private JSError(
String sourceName, @Nullable Node node, int lineno, int charno,
DiagnosticType type, CheckLevel level, String... arguments) {
this.type = type;
this.node = node;
this.description = type.format.format(arguments);
this.lineNumber = lineno;
this.charno = charno;
this.sourceName = sourceName;
this.level = level == null ? type.level : level;
}
/**
* Creates a JSError for a source file location. Private to avoid
* any entanglement with code outside of the compiler.
*/
private JSError(String sourceName, @Nullable Node node,
DiagnosticType type, String... arguments) {
this(sourceName,
node,
(node != null) ? node.getLineno() : -1,
(node != null) ? node.getCharno() : -1,
type, null, arguments);
}
public DiagnosticType getType() {
return type;
}
/**
* Format a message at the given level.
*
* @return the formatted message or {@code null}
*/
public String format(CheckLevel level, MessageFormatter formatter) {
switch (level) {
case ERROR:
return formatter.formatError(this);
case WARNING:
return formatter.formatWarning(this);
default:
return null;
}
}
@Override
public String toString() {
// TODO(user): remove custom toString.
return type.key + ". " + description + " at " +
(sourceName != null && sourceName.length() > 0 ?
sourceName : "(unknown source)") + " line " +
(lineNumber != -1 ? String.valueOf(lineNumber) : "(unknown line)") +
" : " + (charno != -1 ? String.valueOf(charno) : "(unknown column)");
}
/**
* Get the character number.
*/
public int getCharno() {
return charno;
}
@Override
public boolean equals(Object o) {
// Generated by Intellij IDEA
if (this == o) {
return true;
}
if (o == null || getClass() !=
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> o.getClass()) {
return false;
}
JSError jsError = (JSError) o;
if (charno != jsError.charno) {
return false;
}
if (lineNumber != jsError.lineNumber) {
return false;
}
if (!description.equals(jsError.description)) {
return false;
}
if (level != jsError.level) {
return false;
}
if (sourceName != null ? !sourceName.equals(jsError.sourceName)
: jsError.sourceName != null) {
return false;
}
if (!type.equals(jsError.type)) {
return false;
}
return true;
}
@Override
public int hashCode() {
// Generated by Intellij IDEA
int result = type.hashCode();
result = 31 * result + description.hashCode();
result = 31 * result + (sourceName != null ? sourceName.hashCode() : 0);
result = 31 * result + lineNumber;
result = 31 * result + level.hashCode();
result = 31 * result + charno;
return result;
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>/*
* Copyright 2010 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.collect.Maps;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import java.util.Map;
/**
* Filters warnings based on in-code {@code @suppress} annotations.
* @author nicksantos@google.com (Nick Santos)
*/
class SuppressDocWarningsGuard extends WarningsGuard {
/** Warnings guards for each suppressable warnings group, indexed by name. */
private final Map<String, DiagnosticGroupWarningsGuard> suppressors =
Maps.newHashMap();
/**
* The suppressable groups, indexed by name.
*/
SuppressDocWarningsGuard(Map<String, DiagnosticGroup> suppressableGroups) {
for (Map.Entry<String, DiagnosticGroup> entry :
suppressableGroups.entrySet()) {
suppressors.put(
entry.getKey(),
new DiagnosticGroupWarningsGuard(
entry.getValue(),
CheckLevel.OFF));
}
}
@Override
public CheckLevel level(JSError error) {
Node node = error.node;
if (node != null) {
for (Node current = node;
current != null;
current = current.getParent()) {
int type = current.getType();
JSDocInfo info = null;
// We only care about function annotations at the FUNCTION and SCRIPT
// level. Otherwise, the @suppress annotation has an implicit
// dependency on the exact structure of our AST, and that seems like
// a bad idea
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>.
if (type == Token.FUNCTION) {
info = NodeUtil.getFunctionInfo(current);
} else if (type == Token.SCRIPT) {
info = current.getJSDocInfo();
}
if (info != null) {
for (String suppressor : info.getSuppressions()) {
WarningsGuard guard = suppressors.get(suppressor);
// Some @suppress tags are for other tools, and
// may not have a warnings guard.
if (guard != null) {
CheckLevel newLevel = guard.level(error);
if (newLevel != null) {
return newLevel;
}
}
}
}
}
}
return null;
}
@Override
public int getPriority() {
// Happens after path-based filtering, but before other times
// of filtering.
return WarningsGuard.Priority.SUPPRESS_DOC.value;
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> abstract T processSwitchStatement(SwitchStatement statementNode);
abstract T processThrowStatement(ThrowStatement statementNode);
abstract T processTryStatement(TryStatement statementNode);
abstract T processUnaryExpression(UnaryExpression exprNode);
abstract T processVariableDeclaration(VariableDeclaration declarationNode);
abstract T processVariableInitializer(VariableInitializer initializerNode);
abstract T processWhileLoop(WhileLoop loopNode);
abstract T processWithStatement(WithStatement statementNode);
abstract T processIllegalToken(AstNode node);
public T process(AstNode node) {
switch (node.getType()) {
case Token.ADD:
case Token.AND:
case Token.BITAND:
case Token.BITOR:
case Token.BITXOR:
case Token.COMMA:
case Token.DIV:
case Token.EQ:
case Token.GE:
case Token.GT:
case Token.IN:
case Token.INSTANCEOF:
case Token.LE:
case Token.LSH:
case Token.LT:
case Token.MOD:
case Token.MUL:
case Token.NE:
case Token.OR:
case Token.RSH:
case Token.SHEQ:
case Token.SHNE:
case Token.SUB:
case Token.URSH:
return processInfixExpression((InfixExpression) node);
case Token.ARRAYLIT:
return processArrayLiteral((ArrayLiteral) node);
case Token.ASSIGN:
case Token.ASSIGN_ADD:
case Token.ASSIGN_BITAND:
case Token.ASSIGN_BITOR:
case Token.ASSIGN_BITXOR:
case Token.ASSIGN_DIV:
case Token.ASSIGN_LSH:
case Token.ASSIGN_MOD:
case Token.ASSIGN_MUL:
case Token.ASSIGN_RSH:
case Token.ASSIGN_SUB:
case Token.ASSIGN_URSH:
return processAssignment((Assignment) node);
case Token.BITNOT:
case Token.DEC:
case Token.DELPROP:
case Token.INC:
case Token.NEG:
case Token.NOT:
case Token.POS:
case Token.TYPEOF:
case Token.VOID:
return processUnaryExpression((UnaryExpression) node);
case Token.BLOCK:
if (node instanceof Block) {
return processBlock((Block) node);
} else if
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
private static final Set<String> CONSTRUCTORS_WITHOUT_SIDE_EFFECTS =
new HashSet<String>(Arrays.asList(
"Array",
"Date",
"Error",
"Object",
"RegExp",
"XMLHttpRequest"));
// Utility class; do not instantiate.
private NodeUtil() {}
/**
* Gets the boolean value of a node that represents a expression. This method
* effectively emulates the <code>Boolean()</code> JavaScript cast function.
* Note: unlike getBooleanValue this function does not return UNKNOWN
* for expressions with side-effects.
*/
static TernaryValue getImpureBooleanValue(Node n) {
switch (n.getType()) {
case Token.ASSIGN:
case Token.COMMA:
// For ASSIGN and COMMA the value is the value of the RHS.
return getImpureBooleanValue(n.getLastChild());
case Token.NOT:
TernaryValue value = getImpureBooleanValue(n.getLastChild());
return value.not();
case Token.AND: {
TernaryValue lhs = getImpureBooleanValue(n.getFirstChild());
TernaryValue rhs = getImpureBooleanValue(n.getLastChild());
return lhs.and(rhs);
}
case Token.OR: {
TernaryValue lhs = getImpureBooleanValue(n.getFirstChild());
TernaryValue rhs = getImpureBooleanValue(n.getLastChild());
return lhs.or(rhs);
}
case Token.HOOK: {
TernaryValue trueValue = getImpureBooleanValue(
n.getFirstChild().getNext());
TernaryValue falseValue = getImpureBooleanValue(n.getLastChild());
if (trueValue.equals(falseValue)) {
return trueValue;
} else {
return TernaryValue.UNKNOWN;
}
}
case Token.ARRAYLIT:
case Token.OBJECTLIT:
// ignoring side-effects
return TernaryValue.TRUE;
default:
return getPureBooleanValue(n);
}
}
/**
* Gets the boolean value of a node that represents a literal. This method
* effectively emulates the <code>Boolean()</code> JavaScript cast function
* except it return UNKNOWN for known values with side-effects, use
* getExpressionBooleanValue if you don't care about side
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>-effects.
*/
static TernaryValue getPureBooleanValue(Node n) {
switch (n.getType()) {
case Token.STRING:
return TernaryValue.forBoolean(n.getString().length() > 0);
case Token.NUMBER:
return TernaryValue.forBoolean(n.getDouble() != 0);
case Token.NOT:
return getPureBooleanValue(n.getLastChild()).not();
case Token.NULL:
case Token.FALSE:
case Token.VOID:
return TernaryValue.FALSE;
case Token.NAME:
String name = n.getString();
if ("undefined".equals(name)
|| "NaN".equals(name)) {
// We assume here that programs don't change the value of the keyword
// undefined to something other than the value undefined.
return TernaryValue.FALSE;
} else if ("Infinity".equals(name)) {
return TernaryValue.TRUE;
}
break;
case Token.TRUE:
case Token.REGEXP:
return TernaryValue.TRUE;
case Token.ARRAYLIT:
case Token.OBJECTLIT:
if (!mayHaveSideEffects(n)) {
return TernaryValue.TRUE;
}
}
return TernaryValue.UNKNOWN;
}
/**
* Gets the value of a node as a String, or null if it cannot be converted.
* When it returns a non-null String, this method effectively emulates the
* <code>String()</code> JavaScript cast function.
*/
static String getStringValue(Node n) {
// TODO(user): regex literals as well.
switch (n.getType()) {
case Token.STRING:
return n.getString();
case Token.NAME:
String name = n.getString();
if ("undefined".equals(name)
|| "Infinity".equals(name)
|| "NaN".equals(name)) {
return name;
}
break;
case Token.NUMBER:
return getStringValue(n.getDouble());
case Token.FALSE:
case Token.TRUE:
case Token.NULL:
return Node.tokenToName(n.getType());
case Token.VOID:
return "undefined";
case Token.NOT:
TernaryValue child = getPureBooleanValue(n.getFirstChild());
if (child != TernaryValue
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>.UNKNOWN) {
return child.toBoolean(true) ? "false" : "true"; // reversed.
}
break;
case Token.ARRAYLIT:
return arrayToString(n);
case Token.OBJECTLIT:
return "[object Object]";
}
return null;
}
static String getStringValue(double value) {
long longValue = (long) value;
// Return "1" instead of "1.0"
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
/**
* When converting arrays to string using Array.prototype.toString or
* Array.prototype.join, the rules for conversion to String are different
* than converting each element individually. Specifically, "null" and
* "undefined" are converted to an empty string.
* @param n A node that is a member of an Array.
* @return The string representation.
*/
static String getArrayElementStringValue(Node n) {
return (NodeUtil.isNullOrUndefined(n) || n.getType() == Token.EMPTY)
? "" : getStringValue(n);
}
static String arrayToString(Node literal) {
Node first = literal.getFirstChild();
StringBuilder result = new StringBuilder();
int nextSlot = 0;
int nextSkipSlot = 0;
for (Node n = first; n != null; n = n.getNext()) {
String childValue = getArrayElementStringValue(n);
if (childValue == null) {
return null;
}
if (n != first) {
result.append(',');
}
result.append(childValue);
nextSlot++;
}
return result.toString();
}
/**
* Gets the value of a node as a Number, or null if it cannot be converted.
* When it returns a non-null Double, this method effectively emulates the
* <code>Number()</code> JavaScript cast function.
*/
static Double getNumberValue(Node n) {
switch (n.getType()) {
case Token.TRUE:
return 1.0;
case Token.FALSE:
case Token.NULL:
return 0.0;
case Token.NUMBER:
return n.getDouble();
case Token.VOID:
if (mayHave
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>SideEffects(n.getFirstChild())) {
return null;
} else {
return Double.NaN;
}
case Token.NAME:
// Check for known constants
String name = n.getString();
if (name.equals("undefined")) {
return Double.NaN;
}
if (name.equals("NaN")) {
return Double.NaN;
}
if (name.equals("Infinity")) {
return Double.POSITIVE_INFINITY;
}
return null;
case Token.NEG:
if (n.getChildCount() == 1 && n.getFirstChild().getType() == Token.NAME
&& n.getFirstChild().getString().equals("Infinity")) {
return Double.NEGATIVE_INFINITY;
}
return null;
case Token.NOT:
TernaryValue child = getPureBooleanValue(n.getFirstChild());
if (child != TernaryValue.UNKNOWN) {
return child.toBoolean(true) ? 0.0 : 1.0; // reversed.
}
break;
case Token.STRING:
return getStringNumberValue(n.getString());
case Token.ARRAYLIT:
case Token.OBJECTLIT:
String value = getStringValue(n);
return value != null ? getStringNumberValue(value) : null;
}
return null;
}
static Double getStringNumberValue(String rawJsString) {
if (rawJsString.contains("\u000b")) {
// vertical tab is not always whitespace
return null;
}
String s = trimJsWhiteSpace(rawJsString);
// return ScriptRuntime.toNumber(s);
if (s.length() == 0) {
return 0.0;
}
if (s.length() > 2
&& s.charAt(0) == '0'
&& (s.charAt(1) == 'x' || s.charAt(1) == 'X')) {
// Attempt to convert hex numbers.
try {
return Double.valueOf(Integer.parseInt(s.substring(2), 16));
} catch (NumberFormatException e) {
return Double.NaN;
}
}
if (s.length() > 3
&& (s.charAt(0) == '-' || s.charAt(0) == '+')
&& s.charAt(1) == '0'
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
&& (s.charAt(2) == 'x' || s.charAt(2) == 'X')) {
// hex numbers with explicit signs vary between browsers.
return null;
}
// FireFox and IE treat the "Infinity" differently. FireFox is case
// insensitive, but IE treats "infinity" as NaN. So leave it alone.
if (s.equals("infinity")
|| s.equals("-infinity")
|| s.equals("+infinity")) {
return null;
}
try {
return Double.parseDouble(s);
} catch (NumberFormatException e) {
return Double.NaN;
}
}
static String trimJsWhiteSpace(String s) {
int start = 0;
int end = s.length();
while (end > 0
&& isStrWhiteSpaceChar(s.charAt(end - 1)) == TernaryValue.TRUE) {
end--;
}
while (start < end
&& isStrWhiteSpaceChar(s.charAt(start)) == TernaryValue.TRUE) {
start++;
}
return s.substring(start, end);
}
/**
* Copied from Rhino's ScriptRuntime
*/
static TernaryValue isStrWhiteSpaceChar(int c) {
switch (c) {
case '\u000B': // <VT>
return TernaryValue.UNKNOWN; // IE says "no", EcmaScript says "yes"
case ' ': // <SP>
case '\n': // <LF>
case '\r': // <CR>
case '\t': // <TAB>
case '\u00A0': // <NBSP>
case '\u000C': // <FF>
case '\u2028': // <LS>
case '\u2029': // <PS>
case '\uFEFF': // <BOM>
return TernaryValue.TRUE;
default:
return (Character.getType(c) == Character.SPACE_SEPARATOR)
? TernaryValue.TRUE : TernaryValue.FALSE;
}
}
/**
* Gets the function's name. This method recognizes five forms:
* <ul>
* <li>{@code function name() ...}</li>
* <li>{@code var name = function() ...}</
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
switch (parent.getType()) {
case Token.SET:
case Token.GET:
case Token.STRING:
// Return the name of the literal's key.
return parent.getString();
case Token.NUMBER:
return getStringValue(parent);
}
return null;
}
/**
* Returns true if this is an immutable value.
*/
static boolean isImmutableValue(Node n) {
switch (n.getType()) {
case Token.STRING:
case Token.NUMBER:
case Token.NULL:
case Token.TRUE:
case Token.FALSE:
return true;
case Token.NOT:
return isImmutableValue(n.getFirstChild());
case Token.VOID:
case Token.NEG:
return isImmutableValue(n.getFirstChild());
case Token.NAME:
String name = n.getString();
// We assume here that programs don't change the value of the keyword
// undefined to something other than the value undefined.
return "undefined".equals(name)
|| "Infinity".equals(name)
|| "NaN".equals(name);
}
return false;
}
/**
* Returns true if this is a literal value. We define a literal value
* as any node that evaluates to the same thing regardless of when or
* where it is evaluated. So /xyz/ and [3, 5] are literals, but
* the name a is not.
*
* Function literals do not meet this definition, because they
* lexically capture variables. For example, if you have
* <code>
* function() { return a; }
* </code>
* If it is evaluated in a different scope, then it
* captures a different variable. Even if the function did not read
* any captured vairables directly, it would still fail this definition,
* because it affects the lifecycle of variables in the enclosing scope.
*
* However, a function literal with respect to a particular scope is
* a literal.
*
* @param includeFunctions If true, all function expressions will be
* treated as literals.
*/
static boolean isLiteralValue(Node n, boolean includeFunctions) {
switch (n.getType()) {
case Token.ARRAYLIT:
for (Node child = n.getFirstChild(); child != null;
child = child.getNext()) {
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
if (child.getType() != Token.EMPTY
&& !isLiteralValue(child, includeFunctions)) {
return false;
}
}
return true;
case Token.REGEXP:
// Return true only if all children are const.
for (Node child = n.getFirstChild(); child != null;
child = child.getNext()) {
if (!isLiteralValue(child, includeFunctions)) {
return false;
}
}
return true;
case Token.OBJECTLIT:
// Return true only if all values are const.
for (Node child = n.getFirstChild(); child != null;
child = child.getNext()) {
if (!isLiteralValue(child.getFirstChild(), includeFunctions)) {
return false;
}
}
return true;
case Token.FUNCTION:
return includeFunctions && !NodeUtil.isFunctionDeclaration(n);
default:
return isImmutableValue(n);
}
}
/**
* Determines whether the given value may be assigned to a define.
*
* @param val The value being assigned.
* @param defines The list of names of existing defines.
*/
static boolean isValidDefineValue(Node val, Set<String> defines) {
switch (val.getType()) {
case Token.STRING:
case Token.NUMBER:
case Token.TRUE:
case Token.FALSE:
return true;
// Binary operators are only valid if both children are valid.
case Token.ADD:
case Token.BITAND:
case Token.BITNOT:
case Token.BITOR:
case Token.BITXOR:
case Token.DIV:
case Token.EQ:
case Token.GE:
case Token.GT:
case Token.LE:
case Token.LSH:
case Token.LT:
case Token.MOD:
case Token.MUL:
case Token.NE:
case Token.RSH:
case Token.SHEQ:
case Token.SHNE:
case Token.SUB:
case Token.URSH:
return isValidDefineValue(val.getFirstChild(), defines)
&& isValidDefineValue(val.getLastChild(), defines);
// Uniary operators are valid if the child is valid.
case Token.NOT:
case Token.NEG:
case Token.POS:
return isValidDefineValue(val.getFirstChild(), defines
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>);
// Names are valid if and only if they are defines themselves.
case Token.NAME:
case Token.GETPROP:
if (val.isQualifiedName()) {
return defines.contains(val.getQualifiedName());
}
}
return false;
}
/**
* Returns whether this a BLOCK node with no children.
*
* @param block The node.
*/
static boolean isEmptyBlock(Node block) {
if (block.getType() != Token.BLOCK) {
return false;
}
for (Node n = block.getFirstChild(); n != null; n = n.getNext()) {
if (n.getType() != Token.EMPTY) {
return false;
}
}
return true;
}
static boolean isSimpleOperator(Node n) {
return isSimpleOperatorType(n.getType());
}
/**
* A "simple" operator is one whose children are expressions,
* has no direct side-effects (unlike '+='), and has no
* conditional aspects (unlike '||').
*/
static boolean isSimpleOperatorType(int type) {
switch (type) {
case Token.ADD:
case Token.BITAND:
case Token.BITNOT:
case Token.BITOR:
case Token.BITXOR:
case Token.COMMA:
case Token.DIV:
case Token.EQ:
case Token.GE:
case Token.GETELEM:
case Token.GETPROP:
case Token.GT:
case Token.INSTANCEOF:
case Token.LE:
case Token.LSH:
case Token.LT:
case Token.MOD:
case Token.MUL:
case Token.NE:
case Token.NOT:
case Token.RSH:
case Token.SHEQ:
case Token.SHNE:
case Token.SUB:
case Token.TYPEOF:
case Token.VOID:
case Token.POS:
case Token.NEG:
case Token.URSH:
return true;
default:
return false;
}
}
/**
* Creates an EXPR_RESULT.
*
* @param child The expression itself.
* @return Newly created EXPR node with the child as subexpression.
*/
public static Node newExpr(Node child) {
Node expr = new Node(Token.EXPR_
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>4 call, member () [] .
*/
static int precedence(int type) {
switch (type) {
case Token.COMMA: return 0;
case Token.ASSIGN_BITOR:
case Token.ASSIGN_BITXOR:
case Token.ASSIGN_BITAND:
case Token.ASSIGN_LSH:
case Token.ASSIGN_RSH:
case Token.ASSIGN_URSH:
case Token.ASSIGN_ADD:
case Token.ASSIGN_SUB:
case Token.ASSIGN_MUL:
case Token.ASSIGN_DIV:
case Token.ASSIGN_MOD:
case Token.ASSIGN: return 1;
case Token.HOOK: return 2; // ?: operator
case Token.OR: return 3;
case Token.AND: return 4;
case Token.BITOR: return 5;
case Token.BITXOR: return 6;
case Token.BITAND: return 7;
case Token.EQ:
case Token.NE:
case Token.SHEQ:
case Token.SHNE: return 8;
case Token.LT:
case Token.GT:
case Token.LE:
case Token.GE:
case Token.INSTANCEOF:
case Token.IN: return 9;
case Token.LSH:
case Token.RSH:
case Token.URSH: return 10;
case Token.SUB:
case Token.ADD: return 11;
case Token.MUL:
case Token.MOD:
case Token.DIV: return 12;
case Token.INC:
case Token.DEC:
case Token.NEW:
case Token.DELPROP:
case Token.TYPEOF:
case Token.VOID:
case Token.NOT:
case Token.BITNOT:
case Token.POS:
case Token.NEG: return 13;
case Token.ARRAYLIT:
case Token.CALL:
case Token.EMPTY:
case Token.FALSE:
case Token.FUNCTION:
case Token.GETELEM:
case Token.GETPROP:
case Token.GET_REF:
case Token.IF:
case Token.LP:
case Token.NAME:
case Token.NULL:
case Token.NUMBER:
case Token.OBJECTLIT:
case
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> Token.REGEXP:
case Token.STRING:
case Token.THIS:
case Token.TRUE:
return 15;
default: throw new Error("Unknown precedence for " +
Node.tokenToName(type) +
" (type " + type + ")");
}
}
/**
* Apply the supplied predicate against the potential
* all possible result of the expression.
*/
static boolean valueCheck(Node n, Predicate<Node> p) {
switch (n.getType()) {
case Token.ASSIGN:
case Token.COMMA:
return valueCheck(n.getLastChild(), p);
case Token.AND:
case Token.OR:
return valueCheck(n.getFirstChild(), p)
&& valueCheck(n.getLastChild(), p);
case Token.HOOK:
return valueCheck(n.getFirstChild().getNext(), p)
&& valueCheck(n.getLastChild(), p);
default:
return p.apply(n);
}
}
static class NumbericResultPredicate implements Predicate<Node> {
public boolean apply(Node n) {
return isNumericResultHelper(n);
}
}
static final NumbericResultPredicate NUMBERIC_RESULT_PREDICATE =
new NumbericResultPredicate();
/**
* Returns true if the result of node evaluation is always a number
*/
static boolean isNumericResult(Node n) {
return valueCheck(n, NUMBERIC_RESULT_PREDICATE);
}
static boolean isNumericResultHelper(Node n) {
switch (n.getType()) {
case Token.ADD:
return !mayBeString(n.getFirstChild())
&& !mayBeString(n.getLastChild());
case Token.BITNOT:
case Token.BITOR:
case Token.BITXOR:
case Token.BITAND:
case Token.LSH:
case Token.RSH:
case Token.URSH:
case Token.SUB:
case Token.MUL:
case Token.MOD:
case Token.DIV:
case Token.INC:
case Token.DEC:
case Token.POS:
case Token.NEG:
case Token.NUMBER:
return true;
case Token.NAME:
String name = n.getString();
if (name.equals("NaN")) {
return true;
}
if (name
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>.equals("Infinity")) {
return true;
}
return false;
default:
return false;
}
}
static class BooleanResultPredicate implements Predicate<Node> {
public boolean apply(Node n) {
return isBooleanResultHelper(n);
}
}
static final BooleanResultPredicate BOOLEAN_RESULT_PREDICATE =
new BooleanResultPredicate();
/**
* @return Whether the result of node evaluation is always a boolean
*/
static boolean isBooleanResult(Node n) {
return valueCheck(n, BOOLEAN_RESULT_PREDICATE);
}
static boolean isBooleanResultHelper(Node n) {
switch (n.getType()) {
// Primitives
case Token.TRUE:
case Token.FALSE:
// Comparisons
case Token.EQ:
case Token.NE:
case Token.SHEQ:
case Token.SHNE:
case Token.LT:
case Token.GT:
case Token.LE:
case Token.GE:
// Queryies
case Token.IN:
case Token.INSTANCEOF:
// Inversion
case Token.NOT:
// delete operator returns a boolean.
case Token.DELPROP:
return true;
default:
return false;
}
}
static boolean isUndefined(Node n) {
switch (n.getType()) {
case Token.VOID:
return true;
case Token.NAME:
return n.getString().equals("undefined");
}
return false;
}
static boolean isNull(Node n) {
return n.getType() == Token.NULL;
}
static boolean isNullOrUndefined(Node n) {
return isNull(n) || isUndefined(n);
}
static class MayBeStringResultPredicate implements Predicate<Node> {
public boolean apply(Node n) {
return mayBeStringHelper(n);
}
}
static final MayBeStringResultPredicate MAY_BE_STRING_PREDICATE =
new MayBeStringResultPredicate();
/**
* @returns Whether the results is possibly a string.
*/
static boolean mayBeString(Node n) {
return mayBeString(n, true);
}
static boolean mayBeString(Node n, boolean recurse) {
if (recurse) {
return valueCheck(n, MAY_BE_STRING_PRED
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>ICATE);
} else {
return mayBeStringHelper(n);
}
}
static boolean mayBeStringHelper(Node n) {
return !isNumericResult(n) && !isBooleanResult(n)
&& !isUndefined(n) && !isNull(n);
}
/**
* Returns true if the operator is associative.
* e.g. (a * b) * c = a * (b * c)
* Note: "+" is not associative because it is also the concatenation
* for strings. e.g. "a" + (1 + 2) is not "a" + 1 + 2
*/
static boolean isAssociative(int type) {
switch (type) {
case Token.MUL:
case Token.AND:
case Token.OR:
case Token.BITOR:
case Token.BITXOR:
case Token.BITAND:
return true;
default:
return false;
}
}
/**
* Returns true if the operator is commutative.
* e.g. (a * b) * c = c * (b * a)
* Note 1: "+" is not commutative because it is also the concatenation
* for strings. e.g. "a" + (1 + 2) is not "a" + 1 + 2
* Note 2: only operations on literals and pure functions are commutative.
*/
static boolean isCommutative(int type) {
switch (type) {
case Token.MUL:
case Token.BITOR:
case Token.BITXOR:
case Token.BITAND:
return true;
default:
return false;
}
}
static boolean isAssignmentOp(Node n) {
switch (n.getType()){
case Token.ASSIGN:
case Token.ASSIGN_BITOR:
case Token.ASSIGN_BITXOR:
case Token.ASSIGN_BITAND:
case Token.ASSIGN_LSH:
case Token.ASSIGN_RSH:
case Token.ASSIGN_URSH:
case Token.ASSIGN_ADD:
case Token.ASSIGN_SUB:
case Token.ASSIGN_MUL:
case Token.ASSIGN_DIV:
case Token.ASSIGN_MOD:
return true;
}
return false;
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> representation.
*
* @param operator the operator's token value to convert
* @return the string representation or {@code null} if the token value is
* not an operator
*/
static String opToStr(int operator) {
switch (operator) {
case Token.BITOR: return "|";
case Token.OR: return "||";
case Token.BITXOR: return "^";
case Token.AND: return "&&";
case Token.BITAND: return "&";
case Token.SHEQ: return "===";
case Token.EQ: return "==";
case Token.NOT: return "!";
case Token.NE: return "!=";
case Token.SHNE: return "!==";
case Token.LSH: return "<<";
case Token.IN: return "in";
case Token.LE: return "<=";
case Token.LT: return "<";
case Token.URSH: return ">>>";
case Token.RSH: return ">>";
case Token.GE: return ">=";
case Token.GT: return ">";
case Token.MUL: return "*";
case Token.DIV: return "/";
case Token.MOD: return "%";
case Token.BITNOT: return "~";
case Token.ADD: return "+";
case Token.SUB: return "-";
case Token.POS: return "+";
case Token.NEG: return "-";
case Token.ASSIGN: return "=";
case Token.ASSIGN_BITOR: return "|=";
case Token.ASSIGN_BITXOR: return "^=";
case Token.ASSIGN_BITAND: return "&=";
case Token.ASSIGN_LSH: return "<<=";
case Token.ASSIGN_RSH: return ">>=";
case Token.ASSIGN_URSH: return ">>>=";
case Token.ASSIGN_ADD: return "+=";
case Token.ASSIGN_SUB: return "-=";
case Token.ASSIGN_MUL: return "*=";
case Token.ASSIGN_DIV: return "/=";
case Token.ASSIGN_MOD: return "%=";
case Token.VOID: return "void";
case Token.TYPEOF: return "typeof";
case Token.INSTANCEOF: return "instanceof";
default: return null;
}
}
/**
* Converts an operator's
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> token value (see {@link Token}) to a string
* representation or fails.
*
* @param operator the operator's token value to convert
* @return the string representation
* @throws Error if the token value is not an operator
*/
static String opToStrNoFail(int operator) {
String res = opToStr(operator);
if (res == null) {
throw new Error("Unknown op " + operator + ": " +
Token.name(operator));
}
return res;
}
/**
* @return true if n or any of its children are of the specified type
*/
static boolean containsType(Node node,
int type,
Predicate<Node> traverseChildrenPred) {
return has(node, new MatchNodeType(type), traverseChildrenPred);
}
/**
* @return true if n or any of its children are of the specified type
*/
static boolean containsType(Node node, int type) {
return containsType(node, type, Predicates.<Node>alwaysTrue());
}
/**
* Given a node tree, finds all the VAR declarations in that tree that are
* not in an inner scope. Then adds a new VAR node at the top of the current
* scope that redeclares them, if necessary.
*/
static void redeclareVarsInsideBranch(Node branch) {
Collection<Node> vars = getVarsDeclaredInBranch(branch);
if (vars.isEmpty()) {
return;
}
Node parent = getAddingRoot(branch);
for (Node nameNode : vars) {
Node var = new Node(
Token.VAR,
Node.newString(Token.NAME, nameNode.getString())
.copyInformationFrom(nameNode))
.copyInformationFrom(nameNode);
copyNameAnnotations(nameNode, var.getFirstChild());
parent.addChildToFront(var);
}
}
/**
* Copy any annotations that follow a named value.
* @param source
* @param destination
*/
static void copyNameAnnotations(Node source, Node destination) {
if (source.getBooleanProp(Node.IS_CONSTANT_NAME)) {
destination.putBooleanProp(Node.IS_CONSTANT_NAME, true);
}
}
/**
* Gets a Node at the top of the current scope where we can add new var
* declarations
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> as children.
*/
private static Node getAddingRoot(Node n) {
Node addingRoot = null;
Node ancestor = n;
while (null != (ancestor = ancestor.getParent())) {
int type = ancestor.getType();
if (type == Token.SCRIPT) {
addingRoot = ancestor;
break;
} else if (type == Token.FUNCTION) {
addingRoot = ancestor.getLastChild();
break;
}
}
// make sure that the adding root looks ok
Preconditions.checkState(addingRoot.getType() == Token.BLOCK ||
addingRoot.getType() == Token.SCRIPT);
Preconditions.checkState(addingRoot.getFirstChild() == null ||
addingRoot.getFirstChild().getType() != Token.SCRIPT);
return addingRoot;
}
/** Creates function name(params_0, ..., params_n) { body }. */
public static Node newFunctionNode(String name, List<Node> params,
Node body, int lineno, int charno) {
Node parameterParen = new Node(Token.LP, lineno, charno);
for (Node param : params) {
parameterParen.addChildToBack(param);
}
Node function = new Node(Token.FUNCTION, lineno, charno);
function.addChildrenToBack(
Node.newString(Token.NAME, name, lineno, charno));
function.addChildToBack(parameterParen);
function.addChildToBack(body);
return function;
}
/**
* Creates a node representing a qualified name.
*
* @param name A qualified name (e.g. "foo" or "foo.bar.baz")
* @param lineno The source line offset.
* @param charno The source character offset from start of the line.
* @return A NAME or GETPROP node
*/
public static Node newQualifiedNameNode(
CodingConvention convention, String name, int lineno, int charno) {
int endPos = name.indexOf('.');
if (endPos == -1) {
return newName(convention, name, lineno, charno);
}
Node node = newName(
convention, name.substring(0, endPos), lineno, charno);
int startPos;
do {
startPos = endPos + 1;
endPos = name.indexOf('.', startPos);
String part =
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> (endPos == -1
? name.substring(startPos)
: name.substring(startPos, endPos));
Node propNode = Node.newString(Token.STRING, part, lineno, charno);
if (convention.isConstantKey(part)) {
propNode.putBooleanProp(Node.IS_CONSTANT_NAME, true);
}
node = new Node(Token.GETPROP, node, propNode, lineno, charno);
} while (endPos != -1);
return node;
}
/**
* Creates a node representing a qualified name, copying over the source
* location information from the basis node and assigning the given original
* name to the node.
*
* @param name A qualified name (e.g. "foo" or "foo.bar.baz")
* @param basisNode The node that represents the name as currently found in
* the AST.
* @param originalName The original name of the item being represented by the
* NAME node. Used for debugging information.
*
* @return A NAME or GETPROP node
*/
static Node newQualifiedNameNode(
CodingConvention convention, String name, Node basisNode,
String originalName) {
Node node = newQualifiedNameNode(convention, name, -1, -1);
setDebugInformation(node, basisNode, originalName);
return node;
}
/**
* Gets the root node of a qualified name. Must be either NAME or THIS.
*/
static Node getRootOfQualifiedName(Node qName) {
for (Node current = qName; true;
current = current.getFirstChild()) {
int type = current.getType();
if (type == Token.NAME || type == Token.THIS) {
return current;
}
Preconditions.checkState(type == Token.GETPROP);
}
}
/**
* Sets the debug information (source file info and orignal name)
* on the given node.
*
* @param node The node on which to set the debug information.
* @param basisNode The basis node from which to copy the source file info.
* @param originalName The original name of the node.
*/
static void setDebugInformation(Node node, Node basisNode,
String originalName) {
node.copyInformationFromForTree(basisNode);
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> * some constructor.
*/
static boolean isPrototypePropertyDeclaration(Node n) {
if (!isExprAssign(n)) {
return false;
}
return isPrototypeProperty(n.getFirstChild().getFirstChild());
}
static boolean isPrototypeProperty(Node n) {
String lhsString = n.getQualifiedName();
if (lhsString == null) {
return false;
}
int prototypeIdx = lhsString.indexOf(".prototype.");
return prototypeIdx != -1;
}
/**
* @return The class name part of a qualified prototype name.
*/
static Node getPrototypeClassName(Node qName) {
Node cur = qName;
while (isGetProp(cur)) {
if (cur.getLastChild().getString().equals("prototype")) {
return cur.getFirstChild();
} else {
cur = cur.getFirstChild();
}
}
return null;
}
/**
* @return The string property name part of a qualified prototype name.
*/
static String getPrototypePropertyName(Node qName) {
String qNameStr = qName.getQualifiedName();
int prototypeIdx = qNameStr.lastIndexOf(".prototype.");
int memberIndex = prototypeIdx + ".prototype".length() + 1;
return qNameStr.substring(memberIndex);
}
/**
* Create a node for an empty result expression:
* "void 0"
*/
static Node newUndefinedNode(Node srcReferenceNode) {
Node node = new Node(Token.VOID, Node.newNumber(0));
if (srcReferenceNode != null) {
node.copyInformationFromForTree(srcReferenceNode);
}
return node;
}
/**
* Create a VAR node containing the given name and initial value expression.
*/
static Node newVarNode(String name, Node value) {
Node nodeName = Node.newString(Token.NAME, name);
if (value != null) {
Preconditions.checkState(value.getNext() == null);
nodeName.addChildToBack(value);
nodeName.copyInformationFrom(value);
}
Node var = new Node(Token.VAR, nodeName)
.copyInformationFrom(nodeName);
return var;
}
/**
* A predicate for matching name nodes with the specified node.
*/
private static class MatchNameNode implements Predicate
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS><Node>{
final String name;
MatchNameNode(String name){
this.name = name;
}
public boolean apply(Node n) {
return n.getType() == Token.NAME
&& n.getString().equals(name);
}
}
/**
* A predicate for matching nodes with the specified type.
*/
static class MatchNodeType implements Predicate<Node>{
final int type;
MatchNodeType(int type){
this.type = type;
}
public boolean apply(Node n) {
return n.getType() == type;
}
}
/**
* A predicate for matching var or function declarations.
*/
static class MatchDeclaration implements Predicate<Node> {
public boolean apply(Node n) {
return isFunctionDeclaration(n) || n.getType() == Token.VAR;
}
}
/**
* A predicate for matching anything except function nodes.
*/
static class MatchNotFunction implements Predicate<Node>{
public boolean apply(Node n) {
return !isFunction(n);
}
}
/**
* A predicate for matching statements without exiting the current scope.
*/
static class MatchShallowStatement implements Predicate<Node>{
public boolean apply(Node n) {
Node parent = n.getParent();
return n.getType() == Token.BLOCK
|| (!isFunction(n) && (parent == null
|| isControlStructure(parent)
|| isStatementBlock(parent)));
}
}
/**
* Finds the number of times a type is referenced within the node tree.
*/
static int getNodeTypeReferenceCount(
Node node, int type, Predicate<Node> traverseChildrenPred) {
return getCount(node, new MatchNodeType(type), traverseChildrenPred);
}
/**
* Whether a simple name is referenced within the node tree.
*/
static boolean isNameReferenced(Node node,
String name,
Predicate<Node> traverseChildrenPred) {
return has(node, new MatchNameNode(name), traverseChildrenPred);
}
/**
* Whether a simple name is referenced within the node tree.
*/
static boolean isNameReferenced(Node node, String name) {
return isNameReferenced(node, name, Predicates.<Node>alwaysTrue());
}
/**
* Finds the number of times a simple name is referenced within the node tree.
*/
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> != null) {
sourceName = (String) n.getProp(Node.SOURCENAME_PROP);
n = n.getParent();
}
return sourceName;
}
/**
* A new CALL node with the "FREE_CALL" set based on call target.
*/
static Node newCallNode(Node callTarget, Node... parameters) {
boolean isFreeCall = isName(callTarget);
Node call = new Node(Token.CALL, callTarget);
call.putBooleanProp(Node.FREE_CALL, isFreeCall);
for (Node parameter : parameters) {
call.addChildToBack(parameter);
}
return call;
}
/**
* @return Whether the node is known to be a value that is not referenced
* elsewhere.
*/
static boolean evaluatesToLocalValue(Node value) {
return evaluatesToLocalValue(value, Predicates.<Node>alwaysFalse());
}
/**
* @param locals A predicate to apply to unknown local values.
* @return Whether the node is known to be a value that is not a reference
* outside the expression scope.
*/
static boolean evaluatesToLocalValue(Node value, Predicate<Node> locals) {
switch (value.getType()) {
case Token.ASSIGN:
// A result that is aliased by a non-local name, is the effectively the
// same as returning a non-local name, but this doesn't matter if the
// value is immutable.
return NodeUtil.isImmutableValue(value.getLastChild())
|| (locals.apply(value)
&& evaluatesToLocalValue(value.getLastChild(), locals));
case Token.COMMA:
return evaluatesToLocalValue(value.getLastChild(), locals);
case Token.AND:
case Token.OR:
return evaluatesToLocalValue(value.getFirstChild(), locals)
&& evaluatesToLocalValue(value.getLastChild(), locals);
case Token.HOOK:
return evaluatesToLocalValue(value.getFirstChild().getNext(), locals)
&& evaluatesToLocalValue(value.getLastChild(), locals);
case Token.INC:
case Token.DEC:
if (value.getBooleanProp(Node.INCRDECR_PROP)) {
return evaluatesToLocalValue(value.getFirstChild(), locals);
} else {
return true;
}
case Token.THIS:
return locals.apply(
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>value);
case Token.NAME:
return isImmutableValue(value) || locals.apply(value);
case Token.GETELEM:
case Token.GETPROP:
// There is no information about the locality of object properties.
return locals.apply(value);
case Token.CALL:
return callHasLocalResult(value)
|| isToStringMethodCall(value)
|| locals.apply(value);
case Token.NEW:
return newHasLocalResult(value)
|| locals.apply(value);
case Token.FUNCTION:
case Token.REGEXP:
case Token.ARRAYLIT:
case Token.OBJECTLIT:
// Literals objects with non-literal children are allowed.
return true;
case Token.DELPROP:
case Token.IN:
// TODO(johnlenz): should IN operator be included in #isSimpleOperator?
return true;
default:
// Other op force a local value:
// x = '' + g (x is now an local string)
// x -= g (x is now an local number)
if (isAssignmentOp(value)
|| isSimpleOperator(value)
|| isImmutableValue(value)) {
return true;
}
throw new IllegalStateException(
"Unexpected expression node" + value +
"\n parent:" + value.getParent());
}
}
/**
* Given the first sibling, this returns the nth
* sibling or null if no such sibling exists.
* This is like "getChildAtIndex" but returns null for non-existent indexes.
*/
private static Node getNthSibling(Node first, int index) {
Node sibling = first;
while (index != 0 && sibling != null) {
sibling = sibling.getNext();
index--;
}
return sibling;
}
/**
* Given the function, this returns the nth
* argument or null if no such parameter exists.
*/
static Node getArgumentForFunction(Node function, int index) {
Preconditions.checkState(isFunction(function));
return getNthSibling(
function.getFirstChild().getNext().getFirstChild(), index);
}
/**
* Given the new or call, this returns the nth
* argument of the call or null if no such argument exists.
*/
static Node getArgumentForCallOrNew(Node call, int index) {
Preconditions
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> given request traced in multiple threads. (the results
* will be scattered accross reports).
*
* Java objects do not support destructors (as in C++) so Tracer is not robust
* when exceptions are thrown. Each Tracer object should be wrapped in a
* try/finally block so that if an exception is thrown, the Tracer.stop()
* method is guaranteed to be called.
*
* <p>A thread must call {@link Tracer#initCurrentThreadTrace()} to enable the
* Tracer logging, otherwise Tracer does nothing. The requirement to call
* {@code initCurrentThreadTrace} avoids the situtation where Tracer is called
* without the explicit knowledge of the application authors because they
* happen to use a class in another package that uses Tracer. If {@link
* Tracer#logCurrentThreadTrace} is called without calling {@link
* Tracer#initCurrentThreadTrace()}, then a Third Eye WARNING message is logged,
* which should help track down the problem.
*
*/
final class Tracer {
// package-private for access from unit tests
static final Logger logger =
Logger.getLogger(Tracer.class.getName());
/**
* Whether pretty printing is enabled. This is intended to be set once
* at application startup.
*/
private static volatile boolean defaultPrettyPrint;
/* This list is guaranteed to only increase in length. It contains
* a list of additional statistics that the user wants to keep track
* of.
*/
private static List<TracingStatistic> extraTracingStatistics =
new CopyOnWriteArrayList<TracingStatistic>();
/** Values returned by extraTracingStatistics */
private long[] extraTracingValues;
/** The type for grouping traces, may be null */
private final @Nullable String type;
/** A comment string for the report */
private final String comment;
/** Start time of the trace */
private final long startTimeMs;
/** Stop time of the trace, non-final */
private long stopTimeMs;
/**
* Record our starter thread in order to trap Traces that are started in one
* thread and stopped in another
*/
final Thread startThread;
/**
* We limit the number of events in a Trace in order to catch memory
* leaks (a thread that keeps logging events and never clears them).
* This number is arbitrary and can
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> be increased if necessary (though
* if there are more than 1000 events then the Tracer is probably being
* misused).
*/
static final int MAX_TRACE_SIZE = 1000;
/**
* For unit testing. Can't use {@link com.google.common.time.Clock} because
* this code is in base and has minimal dependencies.
*/
static interface InternalClock {
long currentTimeMillis();
}
/**
* Default clock that calls through to the system clock. Can be overridden
* in unit tests.
*/
static InternalClock clock = new InternalClock() {
public long currentTimeMillis() {
return System.currentTimeMillis();
}
};
/**
* Create and start a tracer.
* Both type and comment may be null. See class comment for usage.
*
* @param type The type for totalling
* @param comment Comment about this tracer
*/
Tracer(@Nullable String type, @Nullable String comment) {
this.type = type;
this.comment = comment == null ? "" : comment;
startTimeMs = clock.currentTimeMillis();
startThread = Thread.currentThread();
if (!extraTracingStatistics.isEmpty()) {
int size = extraTracingStatistics.size();
extraTracingValues = new long[size];
int i = 0;
for (TracingStatistic tracingStatistic : extraTracingStatistics) {
extraTracingValues[i] = tracingStatistic.start(startThread);
i++;
}
}
ThreadTrace trace = getThreadTrace();
// Do nothing if the current thread trace wasn't initialized.
if (!trace.isInitialized()) {
return;
}
// Check if we are creating too many Tracers.
if (trace.events.size() >= MAX_TRACE_SIZE) {
logger.log(Level.WARNING,
"Giant thread trace. Too many Tracers created. "
+ "Clearing to avoid memory leak.",
new Throwable(trace.toString()));
trace.truncateEvents();
}
// Check if we forgot to close the Tracers.
if (trace.outstandingEvents.size() >= MAX_TRACE_SIZE) {
logger.log(Level.WARNING,
"Too many outstanding Tracers. Tracer.stop() is missing "
+ "or Tracer.stop() is not wrapped in a
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>();
// Do nothing if the thread trace was not initialized.
if (!trace.isInitialized()) {
return 0;
}
stopTimeMs = clock.currentTimeMillis();
if (extraTracingValues != null) {
// We use extraTracingValues.length rather than
// extraTracingStatistics.size() because a new statistic may
// have been added
for (int i = 0; i < extraTracingValues.length; i++) {
long value = extraTracingStatistics.get(i).stop(startThread);
extraTracingValues[i] = value - extraTracingValues[i];
}
}
// Do nothing if the thread trace was not initialized.
if (!trace.isInitialized()) {
return 0;
}
trace.endEvent(this, silence_threshold);
return stopTimeMs - startTimeMs;
}
/** Stop the trace using the default silence_threshold
*
* @return The time that this trace actually ran.
*/
long stop() {
return stop(-1);
}
@Override public String toString() {
if (type == null) {
return comment;
} else {
return "[" + type + "] " + comment;
}
}
static void setDefaultSilenceThreshold(int threshold) {
getThreadTrace().defaultSilenceThreshold = threshold;
}
/**
* Initialize the trace associated with the current thread by clearing
* out any existing trace. There shouldn't be a trace so if one is
* found we log it as an error.
*/
static void initCurrentThreadTrace() {
ThreadTrace events = getThreadTrace();
if (!events.isEmpty()) {
logger.log(Level.WARNING,
"Non-empty timer log:\n" + events,
new Throwable());
clearThreadTrace();
// Grab a new thread trace if we find a previous non-empty ThreadTrace.
events = getThreadTrace();
}
// Mark the thread trace as initialized.
events.init();
}
static void initCurrentThreadTrace(int default_silence_threshold) {
initCurrentThreadTrace();
setDefaultSilenceThreshold(default_silence_threshold);
}
/**
* Returns a timer report similar to the one described in the class comment.
*
* @return The timer report as a string
*/
static String getCurrentThreadTraceReport() {
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> null;
}
/** Gets the Stat for a tracer type; never returns null */
static Stat getStatsForType(String type) {
Stat stat = getThreadTrace().stats.get(type);
return stat != null ? stat : ZERO_STAT;
}
private static final Stat ZERO_STAT = new Stat();
/** Return the sec.ms part of time (if time = "20:06:11.566", "11.566") */
private static String formatTime(long time) {
int sec = (int) ((time / 1000) % 60);
int ms = (int) (time % 1000);
return String.format("%02d.%03d", sec, ms);
}
/** An event is created every time a Tracer is created or stopped */
private static final class Event {
boolean isStart; // else is_stop
Tracer tracer;
Event(boolean start, Tracer t) {
isStart = start;
tracer = t;
}
long eventTime() {
return isStart ? tracer.startTimeMs : tracer.stopTimeMs;
}
/**
* Converts the event to a formatted string.
* @param prevEventTime The time of the previous event which appears at
* the left most part of the trace line.
* @param indent The indentation to put before the tracer to show the
* hieararchy.
* @param digitsColWidth How many characters the digits should use.
* @return The formatted string.
*/
String toString(long prevEventTime, String indent, int digitsColWidth) {
StringBuilder sb = new StringBuilder(120);
if (prevEventTime == -1) {
appendSpaces(sb, digitsColWidth);
} else {
sb.append(longToPaddedString(
eventTime() - prevEventTime, digitsColWidth));
}
sb.append(' ');
sb.append(formatTime(eventTime()));
if (isStart) {
sb.append(" Start ");
appendSpaces(sb, digitsColWidth);
sb.append(" ");
} else {
sb.append(" Done ");
long delta = tracer.stopTimeMs - tracer.startTimeMs;
sb.append(longToP
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> event.
*/
void endEvent(Tracer t, int silenceThreshold) {
boolean wasOutstanding = outstandingEvents.remove(t);
if (!wasOutstanding) {
if (isOutstandingEventsTruncated) {
// The events stack overflowed and was truncated, so just log a
// warning. Otherwise, we get an exception which is extremely
// confusing.
logger.log(Level.WARNING,
"event not found, probably because the event stack "
+ "overflowed and was truncated",
new Throwable());
} else {
// throw an exception if the event was not found and the events stack
// is pristine
throw new IllegalStateException();
}
}
long elapsed = t.stopTimeMs - t.startTimeMs;
if (silenceThreshold == -1) { // use default
silenceThreshold = defaultSilenceThreshold;
}
if (elapsed < silenceThreshold) {
// If this one is silent then we need to remove the start Event
boolean removed = false;
for (int i = 0; i < events.size(); i++) {
Event e = events.get(i);
if (e.tracer == t) {
Preconditions.checkState(e.isStart);
events.remove(i);
removed = true;
break;
}
}
// Only assert if we didn't find the original and the events
// weren't truncated.
Preconditions.checkState(removed || isEventsTruncated);
} else {
events.add(new Event(false, t));
}
if (t.type != null) {
Stat stat = stats.get(t.type);
if (stat == null) {
stat = new Stat();
if (!extraTracingStatistics.isEmpty()) {
stat.extraInfo = new int[extraTracingStatistics.size()];
}
stats.put(t.type, stat);
}
stat.count++;
if (typeToCountMap != null) {
typeToCountMap.incrementBy(t.type, 1);
}
stat.clockTime += elapsed;
if (typeToTimeMap != null) {
typeToTimeMap.incrementBy(t.type, elapsed);
}
if (stat.extraInfo != null && t.extraTracingValues != null) {
int overlapLength =
Math.
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>min(stat.extraInfo.length, t.extraTracingValues.length);
for (int i = 0; i < overlapLength; i++) {
stat.extraInfo[i] += t.extraTracingValues[i];
AtomicTracerStatMap map =
extraTracingStatistics.get(i).getTracingStat();
if (map != null) {
map.incrementBy(t.type, t.extraTracingValues[i]);
}
}
}
if (elapsed < silenceThreshold) {
stat.silent++;
if (typeToSilentMap != null) {
typeToSilentMap.incrementBy(t.type, 1);
}
}
}
}
boolean isEmpty() {
return events.size() == 0 && outstandingEvents.size() == 0;
}
void truncateOutstandingEvents() {
isOutstandingEventsTruncated = true;
outstandingEvents.clear();
}
void truncateEvents() {
isEventsTruncated = true;
events.clear();
}
/** Produces the lovely Trace seen in the class comments */
// Nullness checker does not understand that prettyPrint => indent != null
@SuppressWarnings("nullness")
@Override public String toString() {
int numDigits = getMaxDigits();
StringBuilder sb = new StringBuilder();
long etime = -1;
LinkedList<String> indent = prettyPrint ? new LinkedList<String>() : null;
for (Event e : events) {
if (prettyPrint && !e.isStart && !indent.isEmpty()) {
indent.pop();
}
sb.append(" ");
if (prettyPrint) {
sb.append(e.toString(etime, Joiner.on("").join(indent), numDigits));
} else {
sb.append(e.toString(etime, "", 4));
}
etime = e.eventTime();
sb.append('\n');
if (prettyPrint && e.isStart) {
indent.push("| ");
}
}
if (outstandingEvents.size() != 0) {
long now = clock.currentTimeMillis();
sb.append(" Unstopped timers:\n");
for (Tracer t : outstandingEvents) {
sb.append(" ").
append(t).
append(" (").
append(now - t.startTimeMs).
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> CodeGenerator(CodeConsumer consumer) {
this(consumer, null);
}
/**
* Insert a ECMASCRIPT 5 strict annotation.
*/
public void tagAsStrict() {
add("'use strict';");
}
void add(String str) {
cc.add(str);
}
private void addIdentifier(String identifier) {
cc.addIdentifier(identifierEscape(identifier));
}
void add(Node n) {
add(n, Context.OTHER);
}
void add(Node n, Context context) {
if (!cc.continueProcessing()) {
return;
}
int type = n.getType();
String opstr = NodeUtil.opToStr(type);
int childCount = n.getChildCount();
Node first = n.getFirstChild();
Node last = n.getLastChild();
// Handle all binary operators
if (opstr != null && first != last) {
Preconditions.checkState(
childCount == 2,
"Bad binary operator \"%s\": expected 2 arguments but got %s",
opstr, childCount);
int p = NodeUtil.precedence(type);
addLeftExpr(first, p, context);
cc.addOp(opstr, true);
// For right-hand-side of operations, only pass context if it's
// the IN_FOR_INIT_CLAUSE one.
Context rhsContext = getContextForNoInOperator(context);
// Handle associativity.
// e.g. if the parse tree is a * (b * c),
// we can simply generate a * b * c.
if (last.getType() == type &&
NodeUtil.isAssociative(type)) {
addExpr(last, p, rhsContext);
} else if (NodeUtil.isAssignmentOp(n) && NodeUtil.isAssignmentOp(last)) {
// Assignments are the only right-associative binary operators
addExpr(last, p, rhsContext);
} else {
addExpr(last, p + 1, rhsContext);
}
return;
}
cc.startSourceMapping(n);
switch (type) {
case Token.TRY: {
Preconditions.checkState(first.getNext().getType() == Token.BLOCK &&
!first.getNext().hasMoreThanOneChild());
Preconditions.
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> Token.LP:
add("(");
addList(first);
add(")");
break;
case Token.COMMA:
Preconditions.checkState(childCount == 2);
addList(first, false, context);
break;
case Token.NUMBER:
Preconditions.checkState(childCount == 0);
cc.addNumber(n.getDouble());
break;
case Token.TYPEOF:
case Token.VOID:
case Token.NOT:
case Token.BITNOT:
case Token.POS: {
// All of these unary operators are right-associative
Preconditions.checkState(childCount == 1);
cc.addOp(NodeUtil.opToStrNoFail(type), false);
addExpr(first, NodeUtil.precedence(type));
break;
}
case Token.NEG: {
Preconditions.checkState(childCount == 1);
// It's important to our sanity checker that the code
// we print produces the same AST as the code we parse back.
// NEG is a weird case because Rhino parses "- -2" as "2".
if (n.getFirstChild().getType() == Token.NUMBER) {
cc.addNumber(-n.getFirstChild().getDouble());
} else {
cc.addOp(NodeUtil.opToStrNoFail(type), false);
addExpr(first, NodeUtil.precedence(type));
}
break;
}
case Token.HOOK: {
Preconditions.checkState(childCount == 3);
int p = NodeUtil.precedence(type);
addLeftExpr(first, p + 1, context);
cc.addOp("?", true);
addExpr(first.getNext(), 1);
cc.addOp(":", true);
addExpr(last, 1);
break;
}
case Token.REGEXP:
if (first.getType() != Token.STRING ||
last.getType() != Token.STRING) {
throw new Error("Expected children to be strings");
}
String regexp = regexpEscape(first.getString(), outputCharsetEncoder);
// I only use one .add because whitespace matters
if (childCount == 2) {
add(regexp + last.getString());
} else {
Preconditions.checkState(childCount == 1);
add
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>(regexp);
}
break;
case Token.GET_REF:
add(first);
break;
case Token.REF_SPECIAL:
Preconditions.checkState(childCount == 1);
add(first);
add(".");
add((String) n.getProp(Node.NAME_PROP));
break;
case Token.FUNCTION:
if (n.getClass() != Node.class) {
throw new Error("Unexpected Node subclass.");
}
Preconditions.checkState(childCount == 3);
boolean funcNeedsParens = (context == Context.START_OF_EXPR);
if (funcNeedsParens) {
add("(");
}
add("function");
add(first);
add(first.getNext());
add(last, Context.PRESERVE_BLOCK);
cc.endFunction(context == Context.STATEMENT);
if (funcNeedsParens) {
add(")");
}
break;
case Token.GET:
case Token.SET:
Preconditions.checkState(n.getParent().getType() == Token.OBJECTLIT);
Preconditions.checkState(childCount == 1);
Preconditions.checkState(first.getType() == Token.FUNCTION);
// Get methods are unnamed
Preconditions.checkState(first.getFirstChild().getString().isEmpty());
if (type == Token.GET) {
// Get methods have no parameters.
Preconditions.checkState(!first.getChildAtIndex(1).hasChildren());
add("get ");
} else {
// Set methods have one parameter.
Preconditions.checkState(first.getChildAtIndex(1).hasOneChild());
add("set ");
}
// The name is on the GET or SET node.
String name = n.getString();
Node fn = first;
Node parameters = fn.getChildAtIndex(1);
Node body = fn.getLastChild();
// Add the property name.
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
// do not encode literally any non-literal characters that were
// unicode escaped.
NodeUtil.isLatin(name)) {
add(name);
} else {
// Determine if the string is a simple number.
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>(d);
} else {
add(jsString(n.getString(), outputCharsetEncoder));
}
}
add(parameters);
add(body, Context.PRESERVE_BLOCK);
break;
case Token.SCRIPT:
case Token.BLOCK: {
if (n.getClass() != Node.class) {
throw new Error("Unexpected Node subclass.");
}
boolean preserveBlock = context == Context.PRESERVE_BLOCK;
if (preserveBlock) {
cc.beginBlock();
}
boolean preferLineBreaks =
type == Token.SCRIPT ||
(type == Token.BLOCK &&
!preserveBlock &&
n.getParent() != null &&
n.getParent().getType() == Token.SCRIPT);
for (Node c = first; c != null; c = c.getNext()) {
add(c, Context.STATEMENT);
// VAR doesn't include ';' since it gets used in expressions
if (c.getType() == Token.VAR) {
cc.endStatement();
}
if (c.getType() == Token.FUNCTION) {
cc.maybeLineBreak();
}
// Prefer to break lines in between top-level statements
// because top level statements are more homogeneous.
if (preferLineBreaks) {
cc.notePreferredLineBreak();
}
}
if (preserveBlock) {
cc.endBlock(cc.breakAfterBlockFor(n, context == Context.STATEMENT));
}
break;
}
case Token.FOR:
if (childCount == 4) {
add("for(");
if (first.getType() == Token.VAR) {
add(first, Context.IN_FOR_INIT_CLAUSE);
} else {
addExpr(first, 0, Context.IN_FOR_INIT_CLAUSE);
}
add(";");
add(first.getNext());
add(";");
add(first.getNext().getNext());
add(")");
addNonEmptyStatement(
last, getContextForNonEmptyExpression(context), false);
} else {
Preconditions.checkState(childCount == 3);
add("for(");
add(first);
add("in");
add(first.getNext());
add(")");
addNonEmptyStatement(
last, getContextForNonEmptyExpression(context), false);
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> }
break;
case Token.DO:
Preconditions.checkState(childCount == 2);
add("do");
addNonEmptyStatement(first, Context.OTHER, false);
add("while(");
add(last);
add(")");
cc.endStatement();
break;
case Token.WHILE:
Preconditions.checkState(childCount == 2);
add("while(");
add(first);
add(")");
addNonEmptyStatement(
last, getContextForNonEmptyExpression(context), false);
break;
case Token.EMPTY:
Preconditions.checkState(childCount == 0);
break;
case Token.GETPROP: {
Preconditions.checkState(
childCount == 2,
"Bad GETPROP: expected 2 children, but got %s", childCount);
Preconditions.checkState(
last.getType() == Token.STRING,
"Bad GETPROP: RHS should be STRING");
boolean needsParens = (first.getType() == Token.NUMBER);
if (needsParens) {
add("(");
}
addLeftExpr(first, NodeUtil.precedence(type), context);
if (needsParens) {
add(")");
}
add(".");
addIdentifier(last.getString());
break;
}
case Token.GETELEM:
Preconditions.checkState(
childCount == 2,
"Bad GETELEM: expected 2 children but got %s", childCount);
addLeftExpr(first, NodeUtil.precedence(type), context);
add("[");
add(first.getNext());
add("]");
break;
case Token.WITH:
Preconditions.checkState(childCount == 2);
add("with(");
add(first);
add(")");
addNonEmptyStatement(
last, getContextForNonEmptyExpression(context), false);
break;
case Token.INC:
case Token.DEC: {
Preconditions.checkState(childCount == 1);
String o = type == Token.INC ? "++" : "--";
int postProp = n.getIntProp(Node.INCRDECR_PROP);
// A non-zero post-prop value indicates a post inc/dec, default of zero
// is a pre-inc/dec.
if (post
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>Prop != 0) {
addLeftExpr(first, NodeUtil.precedence(type), context);
cc.addOp(o, false);
} else {
cc.addOp(o, false);
add(first);
}
break;
}
case Token.CALL:
// We have two special cases here:
// 1) If the left hand side of the call is a direct reference to eval,
// then it must have a DIRECT_EVAL annotation. If it does not, then
// that means it was originally an indirect call to eval, and that
// indirectness must be preserved.
// 2) If the left hand side of the call is a property reference,
// then the call must not a FREE_CALL annotation. If it does, then
// that means it was originally an call without an explicit this and
// that must be preserved.
if (isIndirectEval(first)
|| n.getBooleanProp(Node.FREE_CALL) && NodeUtil.isGet(first)) {
add("(0,");
addExpr(first, NodeUtil.precedence(Token.COMMA));
add(")");
} else {
addLeftExpr(first, NodeUtil.precedence(type), context);
}
add("(");
addList(first.getNext());
add(")");
break;
case Token.IF:
boolean hasElse = childCount == 3;
boolean ambiguousElseClause =
context == Context.BEFORE_DANGLING_ELSE && !hasElse;
if (ambiguousElseClause) {
cc.beginBlock();
}
add("if(");
add(first);
add(")");
if (hasElse) {
addNonEmptyStatement(
first.getNext(), Context.BEFORE_DANGLING_ELSE, false);
add("else");
addNonEmptyStatement(
last, getContextForNonEmptyExpression(context), false);
} else {
addNonEmptyStatement(first.getNext(), Context.OTHER, false);
Preconditions.checkState(childCount == 2);
}
if (ambiguousElseClause) {
cc.endBlock();
}
break;
case Token.NULL:
case Token.THIS:
case Token.FALSE:
case Token.TRUE:
Preconditions.checkState(childCount == 0);
add
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>(Node.tokenToName(type));
break;
case Token.CONTINUE:
Preconditions.checkState(childCount <= 1);
add("continue");
if (childCount == 1) {
if (first.getType() != Token.LABEL_NAME) {
throw new Error("Unexpected token type. Should be LABEL_NAME.");
}
add(" ");
add(first);
}
cc.endStatement();
break;
case Token.DEBUGGER:
Preconditions.checkState(childCount == 0);
add("debugger");
cc.endStatement();
break;
case Token.BREAK:
Preconditions.checkState(childCount <= 1);
add("break");
if (childCount == 1) {
if (first.getType() != Token.LABEL_NAME) {
throw new Error("Unexpected token type. Should be LABEL_NAME.");
}
add(" ");
add(first);
}
cc.endStatement();
break;
case Token.EXPR_VOID:
throw new Error("Unexpected EXPR_VOID. Should be EXPR_RESULT.");
case Token.EXPR_RESULT:
Preconditions.checkState(childCount == 1);
add(first, Context.START_OF_EXPR);
cc.endStatement();
break;
case Token.NEW:
add("new ");
int precedence = NodeUtil.precedence(type);
// If the first child contains a CALL, then claim higher precedence
// to force parentheses. Otherwise, when parsed, NEW will bind to the
// first viable parentheses (don't traverse into functions).
if (NodeUtil.containsType(first, Token.CALL, new MatchNotFunction())) {
precedence = NodeUtil.precedence(first.getType()) + 1;
}
addExpr(first, precedence);
// '()' is optional when no arguments are present
Node next = first.getNext();
if (next != null) {
add("(");
addList(next);
add(")");
}
break;
case Token.STRING:
if (childCount !=
((n.getParent() != null &&
n.getParent().getType() == Token.OBJECTLIT) ? 1 : 0)) {
throw new IllegalStateException(
"Unexpected String children: " + n.getParent().toStringTree());
}
add(jsString
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> (first.getType() != Token.LABEL_NAME) {
throw new Error("Unexpected token type. Should be LABEL_NAME.");
}
add(first);
add(":");
addNonEmptyStatement(
last, getContextForNonEmptyExpression(context), true);
break;
// This node is auto generated in anonymous functions and should just get
// ignored for our purposes.
case Token.SETNAME:
break;
default:
throw new Error("Unknown type " + type + "\n" + n.toStringTree());
}
cc.endSourceMapping(n);
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0;
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
}
return Double.NaN;
}
/**
* @return Whether the name is an indirect eval.
*/
private boolean isIndirectEval(Node n) {
return n.getType() == Token.NAME && "eval".equals(n.getString()) &&
!n.getBooleanProp(Node.DIRECT_EVAL);
}
/**
* Adds a block or expression, substituting a VOID with an empty statement.
* This is used for "for (...);" and "if (...);" type statements.
*
* @param n The node to print.
* @param context The context to determine how the node should be printed.
*/
private void addNonEmptyStatement(
Node n, Context context, boolean allowNonBlockChild) {
Node nodeToProcess = n;
if (!allowNonBlockChild && n.getType() != Token.BLOCK) {
throw new Error("Missing BLOCK child.");
}
// Strip unneeded blocks, that is blocks with <2 children unless
// the CodePrinter specifically wants to keep them.
if (n.getType() == Token.BLOCK) {
int count = getNonEmptyChildCount(n, 2);
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
return true;
}
@Override
public ObjectType getImplicitPrototype() {
return registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE);
}
@Override
boolean defineProperty(String propertyName, JSType type,
boolean inferred, boolean inExterns, Node propertyNode) {
if (isFrozen) {
return false;
}
if (!inferred) {
properties.put(propertyName, type);
}
return super.defineProperty(propertyName, type, inferred, inExterns,
propertyNode);
}
@Override
public JSType getLeastSupertype(JSType that) {
if (!that.isRecordType()) {
return super.getLeastSupertype(that);
}
RecordType thatRecord = (RecordType) that;
RecordTypeBuilder builder = new RecordTypeBuilder(registry);
// The least supertype consist of those properties of the record
// type that both record types hold in common both by name and
// type of the properties themselves.
for (String property : properties.keySet()) {
if (thatRecord.hasProperty(property) &&
thatRecord.getPropertyType(property).isEquivalentTo(
getPropertyType(property))) {
builder.addProperty(property, getPropertyType(property),
getPropertyNode(property));
}
}
return builder.build();
}
@Override
public JSType getGreatestSubtype(JSType that) {
if (that.isRecordType()) {
RecordType thatRecord = (RecordType) that;
RecordTypeBuilder builder = new RecordTypeBuilder(registry);
// The greatest subtype consists of those *unique* properties of both
// record types. If any property conflicts, then the NO_TYPE type
// is returned.
for (String property : properties.keySet()) {
if (thatRecord.hasProperty(property) &&
!thatRecord.getPropertyType(property).isEquivalentTo(
getPropertyType(property))) {
return registry.getNativeObjectType(JSTypeNative.NO_TYPE);
}
builder.addProperty(property, getPropertyType(property),
getPropertyNode(property));
}
for (String property : thatRecord.properties.keySet()) {
if (!hasProperty(property)) {
builder.addProperty(property, thatRecord.getPropertyType(property),
thatRecord.getPropertyNode(property));
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>> scope) {
for (Map.Entry<String, JSType> entry : properties.entrySet()) {
JSType type = entry.getValue();
JSType resolvedType = type.resolve(t, scope);
if (type != resolvedType) {
properties.put(entry.getKey(), resolvedType);
}
}
return super.resolveInternal(t, scope);
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp.graph;
import java.util.Collection;
/**
* A minimal graph interface. Provided is add nodes to the graph, adjacency
* calculation between a SubGraph and a GraphNode, and adding node annotations.
*
* <p>For a more extensive interface, see {@link Graph}.
*
*
* @param <N> Value type that the graph node stores.
* @param <E> Value type that the graph edge stores.
* @see Graph
*/
public interface AdjacencyGraph<N, E> {
/** Gets an immutable list of all nodes. */
Collection<GraphNode<N, E>> getNodes();
/**
* Gets a node from the graph given a value. Values equality are compared
* using <code>Object.equals</code>.
*
* @param value The node's value.
* @return The corresponding node in the graph, null if there value has no
* corresponding node.
*/
GraphNode<N, E> getNode(N value);
/** Returns an empty SubGraph for this Graph. */
SubGraph<N, E> newSubGraph();
/** Makes each node's annotation null. */
void clearNodeAnnotations();
/**
* Returns a weight for the given value to be used in ordering nodes, e.g.
* in {@link GraphColoring}.
*/
int getWeight(N value);
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> greatest type (top) and is never a subtype of
* another except itself or the Unknown type or a named alias.
* @return {@code this.isEquivalentTo(that)}
*/
@Override
public boolean isSubtype(JSType that) {
return JSType.isSubtype(this, that);
}
@Override
public boolean isAllType() {
return true;
}
@Override
public boolean matchesStringContext() {
// Be lenient.
return true;
}
@Override
public boolean matchesObjectContext() {
// Be lenient.
return true;
}
@Override
public boolean canBeCalled() {
return false;
}
@Override
public TernaryValue testForEquality(JSType that) {
return UNKNOWN;
}
@Override
public String toString() {
return "*";
}
@Override
public String getDisplayName() {
return "<Any Type>";
}
@Override
public boolean hasDisplayName() {
return true;
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseAllType();
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
return BooleanLiteralSet.BOTH;
}
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
return this;
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> "tweak ID contains illegal characters. Only letters, numbers, _ " +
"and . are allowed");
/**
* An enum of goog.tweak functions.
*/
private static enum TweakFunction {
REGISTER_BOOLEAN("goog.tweak.registerBoolean", "boolean", Token.TRUE,
Token.FALSE),
REGISTER_NUMBER("goog.tweak.registerNumber", "number", Token.NUMBER),
REGISTER_STRING("goog.tweak.registerString", "string", Token.STRING),
OVERRIDE_DEFAULT_VALUE("goog.tweak.overrideDefaultValue"),
GET_COMPILER_OVERRIDES("goog.tweak.getCompilerOverrides_"),
GET_BOOLEAN("goog.tweak.getBoolean", REGISTER_BOOLEAN),
GET_NUMBER("goog.tweak.getNumber", REGISTER_NUMBER),
GET_STRING("goog.tweak.getString", REGISTER_STRING);
final String name;
final String expectedTypeName;
final int validNodeTypeA;
final int validNodeTypeB;
final TweakFunction registerFunction;
TweakFunction(String name) {
this(name, null, Token.ERROR, Token.ERROR, null);
}
TweakFunction(String name, String expectedTypeName,
int validNodeTypeA) {
this(name, expectedTypeName, validNodeTypeA, Token.ERROR, null);
}
TweakFunction(String name, String expectedTypeName,
int validNodeTypeA, int validNodeTypeB) {
this(name, expectedTypeName, validNodeTypeA, validNodeTypeB, null);
}
TweakFunction(String name, TweakFunction registerFunction) {
this(name, null, Token.ERROR, Token.ERROR, registerFunction);
}
TweakFunction(String name, String expectedTypeName,
int validNodeTypeA, int validNodeTypeB,
TweakFunction registerFunction) {
this.name = name;
this.expectedTypeName = expectedTypeName;
this.validNodeTypeA = validNodeTypeA;
this.validNodeTypeB = validNodeTypeB;
this.registerFunction = registerFunction;
}
boolean isValidNodeType(int type) {
return type == validNodeTypeA || type == validNodeTypeB;
}
boolean isCorrectRegisterFunction(TweakFunction registerFunction) {
Preconditions.checkNotNull(registerFunction);
return this.registerFunction == registerFunction;
}
boolean isGetterFunction
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>String, TweakInfo> tweakInfos) {
for (Entry<String, Node> entry : compilerDefaultValueOverrides.entrySet()) {
String tweakId = entry.getKey();
TweakInfo tweakInfo = tweakInfos.get(tweakId);
if (tweakInfo == null) {
compiler.report(JSError.make(UNKNOWN_TWEAK_WARNING, tweakId));
} else {
TweakFunction registerFunc = tweakInfo.registerCall.tweakFunc;
Node value = entry.getValue();
if (!registerFunc.isValidNodeType(value.getType())) {
compiler.report(JSError.make(INVALID_TWEAK_DEFAULT_VALUE_WARNING,
tweakId, registerFunc.getName(),
registerFunc.getExpectedTypeName()));
} else {
tweakInfo.defaultValueNode = value;
}
}
}
}
/**
* Finds all calls to goog.tweak functions and emits warnings/errors if any
* of the calls have issues.
* @return A map of {@link TweakInfo} structures, keyed by tweak ID.
*/
private CollectTweaksResult collectTweaks(Node root) {
CollectTweaks pass = new CollectTweaks();
NodeTraversal.traverse(compiler, root, pass);
Map<String, TweakInfo> tweakInfos = pass.allTweaks;
for (TweakInfo tweakInfo: tweakInfos.values()) {
tweakInfo.emitAllWarnings();
}
return new CollectTweaksResult(tweakInfos, pass.getOverridesCalls);
}
private final static class CollectTweaksResult {
final Map<String, TweakInfo> tweakInfos;
final List<TweakFunctionCall> getOverridesCalls;
CollectTweaksResult(Map<String, TweakInfo> tweakInfos,
List<TweakFunctionCall> getOverridesCalls) {
this.tweakInfos = tweakInfos;
this.getOverridesCalls = getOverridesCalls;
}
}
/**
* Processes all calls to goog.tweak functions.
*/
private final class CollectTweaks extends AbstractPostOrderCallback {
final Map<String, TweakInfo> allTweaks = Maps.newHashMap();
final List<TweakFunctionCall> getOverridesCalls = Lists.newArrayList();
@Override
public void visit(NodeTraversal t, Node n,
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> information should be generated.
* <p>
* Without source information, evaluating the "toString" method
* on JavaScript functions produces only "[native code]" for
* the body of the function.
* Note that code generated without source is not fully ECMA
* conformant.
* @since 1.3
*/
public final void setGeneratingSource(boolean generatingSource)
{
if (sealed) onSealedMutation();
this.generatingSource = generatingSource;
}
/**
* Get the current optimization level.
* <p>
* The optimization level is expressed as an integer between -1 and
* 9.
* @since 1.3
*
*/
public final int getOptimizationLevel()
{
return optimizationLevel;
}
public static boolean isValidOptimizationLevel(int optimizationLevel)
{
return -1 <= optimizationLevel && optimizationLevel <= 9;
}
public static void checkOptimizationLevel(int optimizationLevel)
{
if (isValidOptimizationLevel(optimizationLevel)) {
return;
}
throw new IllegalArgumentException(
"Optimization level outside [-1..9]: "+optimizationLevel);
}
/**
* Get a value corresponding to a key.
* <p>
* Since the Context is associated with a thread it can be
* used to maintain values that can be later retrieved using
* the current thread.
* <p>
* Note that the values are maintained with the Context, so
* if the Context is disassociated from the thread the values
* cannot be retreived. Also, if private data is to be maintained
* in this manner the key should be a java.lang.Object
* whose reference is not divulged to untrusted code.
* @param key the key used to lookup the value
* @return a value previously stored using putThreadLocal.
*/
public final Object getThreadLocal(Object key)
{
if (hashtable == null)
return null;
return hashtable.get(key);
}
/**
* Put a value that can later be retrieved using a given key.
* <p>
* @param key the key used to index the value
* @param value the value to save
*/
public final void putThreadLocal(Object key, Object value)
{
if (sealed) onSealedMutation();
if (hashtable
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> == null)
hashtable = new Hashtable<Object, Object>();
hashtable.put(key, value);
}
/**
* Remove values from thread-local storage.
* @param key the key for the entry to remove.
* @since 1.5 release 2
*/
public final void removeThreadLocal(Object key)
{
if (sealed) onSealedMutation();
if (hashtable == null)
return;
hashtable.remove(key);
}
/**
* @deprecated
* @see #FEATURE_DYNAMIC_SCOPE
* @see #hasFeature(int)
*/
@Deprecated
public final boolean hasCompileFunctionsWithDynamicScope()
{
return compileFunctionsWithDynamicScopeFlag;
}
/**
* @deprecated
* @see #FEATURE_DYNAMIC_SCOPE
* @see #hasFeature(int)
*/
@Deprecated
public final void setCompileFunctionsWithDynamicScope(boolean flag)
{
if (sealed) onSealedMutation();
compileFunctionsWithDynamicScopeFlag = flag;
}
/**
* Return the debugger context data associated with current context.
* @return the debugger data, or null if debugger is not attached
*/
public final Object getDebuggerContextData()
{
return debuggerData;
}
/**
* Implementation of {@link Context#hasFeature(int featureIndex)}.
* This can be used to customize {@link Context} without introducing
* additional subclasses.
*/
protected boolean hasFeature(int featureIndex)
{
int version;
switch (featureIndex) {
case Context.FEATURE_NON_ECMA_GET_YEAR:
/*
* During the great date rewrite of 1.3, we tried to track the
* evolving ECMA standard, which then had a definition of
* getYear which always subtracted 1900. Which we
* implemented, not realizing that it was incompatible with
* the old behavior... now, rather than thrash the behavior
* yet again, we've decided to leave it with the - 1900
* behavior and point people to the getFullYear method. But
* we try to protect existing scripts that have specified a
* version...
*/
version = getLanguageVersion();
return (version == Context.VERSION_1_0
|| version == Context
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>TAG_EMPTY",
"Could not resolve type in {0} tag of {1}");
static final DiagnosticType IMPLEMENTS_WITHOUT_CONSTRUCTOR =
DiagnosticType.warning(
"JSC_IMPLEMENTS_WITHOUT_CONSTRUCTOR",
"@implements used without @constructor or @interface for {0}");
static final DiagnosticType VAR_ARGS_MUST_BE_LAST = DiagnosticType.warning(
"JSC_VAR_ARGS_MUST_BE_LAST",
"variable length argument must be last");
static final DiagnosticType OPTIONAL_ARG_AT_END = DiagnosticType.warning(
"JSC_OPTIONAL_ARG_AT_END",
"optional arguments must be at the end");
static final DiagnosticType INEXISTANT_PARAM = DiagnosticType.warning(
"JSC_INEXISTANT_PARAM",
"parameter {0} does not appear in {1}''s parameter list");
static final DiagnosticType TYPE_REDEFINITION = DiagnosticType.warning(
"JSC_TYPE_REDEFINITION",
"attempted re-definition of type {0}\n"
+ "found : {1}\n"
+ "expected: {2}");
static final DiagnosticType TEMPLATE_TYPE_DUPLICATED = DiagnosticType.error(
"JSC_TEMPLATE_TYPE_DUPLICATED",
"Only one parameter type must be the template type");
static final DiagnosticType TEMPLATE_TYPE_EXPECTED = DiagnosticType.error(
"JSC_TEMPLATE_TYPE_EXPECTED",
"The template type must be a parameter type");
static final DiagnosticType THIS_TYPE_NON_OBJECT =
DiagnosticType.warning(
"JSC_THIS_TYPE_NON_OBJECT",
"@this type of a function must be an object\n" +
"Actual type: {0}");
private class ExtendedTypeValidator implements Predicate<JSType> {
@Override
public boolean apply(JSType type) {
ObjectType objectType = ObjectType.cast(type);
if (objectType == null) {
reportWarning(EXTENDS_NON_OBJECT, fnName, type.toString());
} else if (
objectType.isEmptyType() ||
(objectType.isUnknownType() &&
// If this has a supertype that hasn't been resolved yet,
// then we can assume this type will be ok once the super
// type resolves.
(object
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>Type.getImplicitPrototype() == null ||
objectType.getImplicitPrototype().isResolved()))) {
reportWarning(RESOLVED_TAG_EMPTY, "@extends", fnName);
} else {
return true;
}
return false;
}
}
private class ImplementedTypeValidator implements Predicate<JSType> {
@Override
public boolean apply(JSType type) {
ObjectType objectType = ObjectType.cast(type);
if (objectType == null) {
reportError(BAD_IMPLEMENTED_TYPE, fnName);
} else if (
objectType.isEmptyType() ||
(objectType.isUnknownType() &&
// If this has a supertype that hasn't been resolved yet,
// then we can assume this type will be ok once the super
// type resolves.
(objectType.getImplicitPrototype() == null ||
objectType.getImplicitPrototype().isResolved()))) {
reportWarning(RESOLVED_TAG_EMPTY, "@implements", fnName);
} else {
return true;
}
return false;
}
}
private class ThisTypeValidator implements Predicate<JSType> {
@Override
public boolean apply(JSType type) {
// TODO(user): Doing an instanceof check here is too
// restrictive as (Date,Error) is, for instance, an object type
// even though its implementation is a UnionType. Would need to
// create interfaces JSType, ObjectType, FunctionType etc and have
// separate implementation instead of the class hierarchy, so that
// union types can also be object types, etc.
if (!type.restrictByNotNullOrUndefined().isSubtype(
typeRegistry.getNativeType(OBJECT_TYPE))) {
reportWarning(THIS_TYPE_NON_OBJECT, type.toString());
return false;
}
return true;
}
}
/**
* @param fnName The function name.
* @param compiler The compiler.
* @param errorRoot The node to associate with any warning generated by
* this builder.
* @param sourceName A source name for associating any warnings that
* we have to emit.
* @param scope The syntactic scope.
*/
FunctionTypeBuilder(String fnName, AbstractCompiler compiler,
Node errorRoot, String sourceName, Scope scope) {
Preconditions.checkNotNull(errorRoot);
this.fnName
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>(VOID_TYPE);
returnTypeInferred = true;
}
}
return this;
}
/**
* Infer the role of the function (whether it's a constructor or interface)
* and what it inherits from in JSDocInfo.
*/
FunctionTypeBuilder inferInheritance(@Nullable JSDocInfo info) {
if (info != null) {
isConstructor = info.isConstructor();
isInterface = info.isInterface();
// base type
if (info.hasBaseType()) {
if (isConstructor || isInterface) {
JSType maybeBaseType =
info.getBaseType().evaluate(scope, typeRegistry);
if (maybeBaseType != null &&
maybeBaseType.setValidator(new ExtendedTypeValidator())) {
baseType = (ObjectType) maybeBaseType;
}
} else {
reportWarning(EXTENDS_WITHOUT_TYPEDEF, fnName);
}
}
// implemented interfaces
if (isConstructor || isInterface) {
implementedInterfaces = Lists.newArrayList();
for (JSTypeExpression t : info.getImplementedInterfaces()) {
JSType maybeInterType = t.evaluate(scope, typeRegistry);
if (maybeInterType != null &&
maybeInterType.setValidator(new ImplementedTypeValidator())) {
implementedInterfaces.add((ObjectType) maybeInterType);
}
}
if (baseType != null) {
JSType maybeFunctionType = baseType.getConstructor();
if (maybeFunctionType instanceof FunctionType) {
FunctionType functionType = baseType.getConstructor();
Iterables.addAll(
implementedInterfaces,
functionType.getImplementedInterfaces());
}
}
} else if (info.getImplementedInterfaceCount() > 0) {
reportWarning(IMPLEMENTS_WITHOUT_CONSTRUCTOR, fnName);
}
}
return this;
}
/**
* Infers the type of {@code this}.
* @param type The type of this.
*/
FunctionTypeBuilder inferThisType(JSDocInfo info, JSType type) {
// Look at the @this annotation first.
inferThisType(info, (Node) null);
if (thisType == null) {
ObjectType objType = ObjectType.cast(type);
if (objType != null && (info == null || !info.hasType())) {
thisType = objType;
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> ref);
}
}
}
}
}
private void reportBadModuleReference(Name name, Ref ref) {
compiler.report(
JSError.make(ref.sourceName, ref.node, STRICT_MODULE_DEP_QNAME,
ref.module.getName(), name.declaration.module.getName(),
name.fullName()));
}
private void reportRefToUndefinedName(Name name, Ref ref) {
// grab the highest undefined ancestor to output in the warning message.
while (name.parent != null &&
name.parent.globalSets + name.parent.localSets == 0) {
name = name.parent;
}
// If this is an annotated EXPR-GET, don't do anything.
Node parent = ref.node.getParent();
if (parent.getType() == Token.EXPR_RESULT) {
JSDocInfo info = ref.node.getJSDocInfo();
if (info != null && info.hasTypedefType()) {
return;
}
}
compiler.report(
JSError.make(ref.sourceName, ref.node, level, UNDEFINED_NAME_WARNING,
name.fullName()));
}
/**
* Checks whether the given name is a property, and whether that property
* must be initialized with its full qualified name.
*/
private static boolean propertyMustBeInitializedByFullName(Name name) {
// If an object literal in the global namespace is never aliased,
// then all of its properties must be defined using its full qualified
// name. This implies that its properties must all be in the global
// namespace as well.
//
// The same is not true for FUNCTION and OTHER types, because their
// implicit prototypes have properties that are not captured by the global
// namespace.
return name.parent != null && name.parent.aliasingGets == 0 &&
name.parent.type == Name.Type.OBJECTLIT;
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>/*
* Copyright 2007 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import com.google.javascript.rhino.jstype.FunctionType;
import com.google.javascript.rhino.jstype.JSTypeNative;
import com.google.javascript.rhino.jstype.ObjectType;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* This describes the Closure-specific JavaScript coding conventions.
*
*/
public class ClosureCodingConvention extends DefaultCodingConvention {
private static final long serialVersionUID = 1L;
private static final String TYPEDEF_NAME = "goog.typedef";
static final DiagnosticType OBJECTLIT_EXPECTED = DiagnosticType.warning(
"JSC_REFLECT_OBJECTLIT_EXPECTED",
"Object literal expected as second argument");
/**
* Closure's goog.inherits adds a {@code superClass_} property to the
* subclass, and a {@code constructor} property.
*/
@Override
public void applySubclassRelationship(FunctionType parentCtor,
FunctionType childCtor, SubclassType type) {
if (type == SubclassType.INHERITS) {
childCtor.defineDeclaredProperty("superClass_",
parentCtor.getPrototype(), false, parentCtor.getSource());
childCtor.getPrototype().defineDeclaredProperty("constructor",
childCtor
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>, false, parentCtor.getSource());
}
}
/**
* {@inheritDoc}
*
* <p>Understands several different inheritance patterns that occur in
* Google code (various uses of {@code inherits} and {@code mixin}).
*/
@Override
public SubclassRelationship getClassesDefinedByCall(Node callNode) {
Node callName = callNode.getFirstChild();
SubclassType type = typeofClassDefiningName(callName);
if (type != null) {
Node subclass = null;
Node superclass = callNode.getLastChild();
// There are six possible syntaxes for a class-defining method:
// SubClass.inherits(SuperClass)
// goog.inherits(SubClass, SuperClass)
// goog$inherits(SubClass, SuperClass)
// SubClass.mixin(SuperClass.prototype)
// goog.mixin(SubClass.prototype, SuperClass.prototype)
// goog$mixin(SubClass.prototype, SuperClass.prototype)
boolean isDeprecatedCall = callNode.getChildCount() == 2 &&
callName.getType() == Token.GETPROP;
if (isDeprecatedCall) {
// SubClass.inherits(SuperClass)
subclass = callName.getFirstChild();
} else if (callNode.getChildCount() == 3) {
// goog.inherits(SubClass, SuperClass)
subclass = callName.getNext();
}
if (type == SubclassType.MIXIN) {
// Only consider mixins that mix two prototypes as related to
// inheritance.
if (!endsWithPrototype(superclass)) {
return null;
}
if (!isDeprecatedCall) {
if (!endsWithPrototype(subclass)) {
return null;
}
// Strip off the prototype from the name.
subclass = subclass.getFirstChild();
}
superclass = superclass.getFirstChild();
}
// bail out if either of the side of the "inherits"
// isn't a real class name. This prevents us from
// doing something weird in cases like:
// goog.inherits(MySubClass, cond ? SuperClass1 : BaseClass2)
if (subclass != null &&
subclass.isUnscopedQualifiedName() &&
superclass.isUnscopedQualifiedName()) {
return new SubclassRelationship(type, subclass, superclass);
}
}
return null;
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
String functionName){
String className = null;
if (NodeUtil.isExprCall(parent)) {
Node callee = node.getFirstChild();
if (callee != null && callee.getType() == Token.GETPROP) {
String qualifiedName = callee.getQualifiedName();
if ((functionName).equals(qualifiedName)) {
className = callee.getNext().getString();
}
}
}
return className;
}
/**
* Use closure's implementation.
* @return closure's function name for exporting properties.
*/
@Override
public String getExportPropertyFunction() {
return "goog.exportProperty";
}
/**
* Use closure's implementation.
* @return closure's function name for exporting symbols.
*/
@Override
public String getExportSymbolFunction() {
return "goog.exportSymbol";
}
@Override
public List<String> identifyTypeDeclarationCall(Node n) {
Node callName = n.getFirstChild();
if ("goog.addDependency".equals(callName.getQualifiedName()) &&
n.getChildCount() >= 3) {
Node typeArray = callName.getNext().getNext();
if (typeArray.getType() == Token.ARRAYLIT) {
List<String> typeNames = Lists.newArrayList();
for (Node name = typeArray.getFirstChild(); name != null;
name = name.getNext()) {
if (name.getType() == Token.STRING) {
typeNames.add(name.getString());
}
}
return typeNames;
}
}
return null;
}
@Override
public String identifyTypeDefAssign(Node n) {
Node firstChild = n.getFirstChild();
int type = n.getType();
if (type == Token.ASSIGN) {
if (TYPEDEF_NAME.equals(n.getLastChild().getQualifiedName())) {
return firstChild.getQualifiedName();
}
} else if (type == Token.VAR && firstChild.hasChildren()) {
if (TYPEDEF_NAME.equals(
firstChild.getFirstChild().getQualifiedName())) {
return firstChild.getString();
}
}
return null;
}
@Override
public String getAbstractMethodName() {
return "goog.abstractMethod";
}
@Override
public String getSingletonGetterClassName(Node callNode) {
Node
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> extends Node {
private static final long serialVersionUID = 1L;
NumberNode(double number) {
super(Token.NUMBER);
this.number = number;
}
public NumberNode(double number, int lineno, int charno) {
super(Token.NUMBER, lineno, charno);
this.number = number;
}
@Override
public double getDouble() {
return this.number;
}
@Override
public void setDouble(double d) {
this.number = d;
}
@Override
boolean isEquivalentTo(Node node, boolean compareJsType, boolean recurse) {
return (super.isEquivalentTo(node, compareJsType, recurse)
&& getDouble() == ((NumberNode) node).getDouble());
}
private double number;
}
private static class StringNode extends Node {
private static final long serialVersionUID = 1L;
StringNode(int type, String str) {
super(type);
if (null == str) {
throw new IllegalArgumentException("StringNode: str is null");
}
this.str = str;
}
StringNode(int type, String str, int lineno, int charno) {
super(type, lineno, charno);
if (null == str) {
throw new IllegalArgumentException("StringNode: str is null");
}
this.str = str;
}
/**
* returns the string content.
* @return non null.
*/
@Override
public String getString() {
return this.str;
}
/**
* sets the string content.
* @param str the new value. Non null.
*/
@Override
public void setString(String str) {
if (null == str) {
throw new IllegalArgumentException("StringNode: str is null");
}
this.str = str;
}
@Override
boolean isEquivalentTo(Node node, boolean compareJsType, boolean recurse) {
return (super.isEquivalentTo(node, compareJsType, recurse)
&& this.str.equals(((StringNode) node).str));
}
/**
* If the property is not defined, this was not a quoted key. The
* QUOTED_PROP int property is only assigned to STRING tokens used as
* object lit keys.
* @return true if this was a quoted
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> string key in an object literal.
*/
@Override
public boolean isQuotedString() {
return getBooleanProp(QUOTED_PROP);
}
/**
* This should only be called for STRING nodes created in object lits.
*/
@Override
public void setQuotedString() {
putBooleanProp(QUOTED_PROP, true);
}
private String str;
}
// PropListItems are immutable so that they can be shared.
private static class PropListItem implements Serializable {
private static final long serialVersionUID = 1L;
final PropListItem next;
final int type;
final int intValue;
final Object objectValue;
PropListItem(int type, int intValue, PropListItem next) {
this(type, intValue, null, next);
}
PropListItem(int type, Object objectValue, PropListItem next) {
this(type, 0, objectValue, next);
}
PropListItem(
int type, int intValue, Object objectValue, PropListItem next) {
this.type = type;
this.intValue = intValue;
this.objectValue = objectValue;
this.next = next;
}
}
public Node(int nodeType) {
type = nodeType;
parent = null;
sourcePosition = -1;
}
public Node(int nodeType, Node child) {
Preconditions.checkArgument(child.parent == null,
"new child has existing parent");
Preconditions.checkArgument(child.next == null,
"new child has existing sibling");
type = nodeType;
parent = null;
first = last = child;
child.next = null;
child.parent = this;
sourcePosition = -1;
}
public Node(int nodeType, Node left, Node right) {
Preconditions.checkArgument(left.parent == null,
"first new child has existing parent");
Preconditions.checkArgument(left.next == null,
"first new child has existing sibling");
Preconditions.checkArgument(right.parent == null,
"second new child has existing parent");
Preconditions.checkArgument(right.next == null,
"second new child has existing sibling");
type = nodeType;
parent = null;
first = left;
last = right;
left.next = right;
left.parent =
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> this;
right.next = null;
right.parent = this;
sourcePosition = -1;
}
public Node(int nodeType, Node left, Node mid, Node right) {
Preconditions.checkArgument(left.parent == null);
Preconditions.checkArgument(left.next == null);
Preconditions.checkArgument(mid.parent == null);
Preconditions.checkArgument(mid.next == null);
Preconditions.checkArgument(right.parent == null);
Preconditions.checkArgument(right.next == null);
type = nodeType;
parent = null;
first = left;
last = right;
left.next = mid;
left.parent = this;
mid.next = right;
mid.parent = this;
right.next = null;
right.parent = this;
sourcePosition = -1;
}
public Node(int nodeType, Node left, Node mid, Node mid2, Node right) {
Preconditions.checkArgument(left.parent == null);
Preconditions.checkArgument(left.next == null);
Preconditions.checkArgument(mid.parent == null);
Preconditions.checkArgument(mid.next == null);
Preconditions.checkArgument(mid2.parent == null);
Preconditions.checkArgument(mid2.next == null);
Preconditions.checkArgument(right.parent == null);
Preconditions.checkArgument(right.next == null);
type = nodeType;
parent = null;
first = left;
last = right;
left.next = mid;
left.parent = this;
mid.next = mid2;
mid.parent = this;
mid2.next = right;
mid2.parent = this;
right.next = null;
right.parent = this;
sourcePosition = -1;
}
public Node(int nodeType, int lineno, int charno) {
type = nodeType;
parent = null;
sourcePosition = mergeLineCharNo(lineno, charno);
}
public Node(int nodeType, Node child, int lineno, int charno) {
this(nodeType, child);
sourcePosition = mergeLineCharNo(lineno, charno);
}
public Node(int nodeType, Node left, Node right, int lineno, int charno) {
this(nodeType, left, right);
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> sourcePosition = mergeLineCharNo(lineno, charno);
}
public Node(int nodeType, Node left, Node mid, Node right,
int lineno, int charno) {
this(nodeType, left, mid, right);
sourcePosition = mergeLineCharNo(lineno, charno);
}
public Node(int nodeType, Node left, Node mid, Node mid2, Node right,
int lineno, int charno) {
this(nodeType, left, mid, mid2, right);
sourcePosition = mergeLineCharNo(lineno, charno);
}
public Node(int nodeType, Node[] children, int lineno, int charno) {
this(nodeType, children);
sourcePosition = mergeLineCharNo(lineno, charno);
}
public Node(int nodeType, Node[] children) {
this.type = nodeType;
parent = null;
if (children.length != 0) {
this.first = children[0];
this.last = children[children.length - 1];
for (int i = 1; i < children.length; i++) {
if (null != children[i - 1].next) {
// fail early on loops. implies same node in array twice
throw new IllegalArgumentException("duplicate child");
}
children[i - 1].next = children[i];
Preconditions.checkArgument(children[i - 1].parent == null);
children[i - 1].parent = this;
}
Preconditions.checkArgument(children[children.length - 1].parent == null);
children[children.length - 1].parent = this;
if (null != this.last.next) {
// fail early on loops. implies same node in array twice
throw new IllegalArgumentException("duplicate child");
}
}
}
public static Node newNumber(double number) {
return new NumberNode(number);
}
public static Node newNumber(double number, int lineno, int charno) {
return new NumberNode(number, lineno, charno);
}
public static Node newString(String str) {
return new StringNode(Token.STRING, str);
}
public static Node newString(int type, String str) {
return new StringNode(type, str);
}
public static Node
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> newString(String str, int lineno, int charno) {
return new StringNode(Token.STRING, str, lineno, charno);
}
public static Node newString(int type, String str, int lineno, int charno) {
return new StringNode(type, str, lineno, charno);
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public boolean hasChildren() {
return first != null;
}
public Node getFirstChild() {
return first;
}
public Node getLastChild() {
return last;
}
public Node getNext() {
return next;
}
public Node getChildBefore(Node child) {
if (child == first) {
return null;
}
Node n = first;
while (n.next != child) {
n = n.next;
if (n == null) {
throw new RuntimeException("node is not a child");
}
}
return n;
}
public Node getChildAtIndex(int i) {
Node n = first;
while (i > 0) {
n = n.next;
i--;
}
return n;
}
public Node getLastSibling() {
Node n = this;
while (n.next != null) {
n = n.next;
}
return n;
}
public void addChildToFront(Node child) {
Preconditions.checkArgument(child.parent == null);
Preconditions.checkArgument(child.next == null);
child.parent = this;
child.next = first;
first = child;
if (last == null) {
last = child;
}
}
public void addChildToBack(Node child) {
Preconditions.checkArgument(child.parent == null);
Preconditions.checkArgument(child.next == null);
child.parent = this;
child.next = null;
if (last == null) {
first = last = child;
return;
}
last.next = child;
last = child;
}
public void addChildrenToFront(Node children) {
for (Node child = children; child != null; child = child.next) {
Preconditions.checkArgument(child.parent == null
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> }
/**
* Detaches child from Node and replaces it with newChild.
*/
public void replaceChild(Node child, Node newChild) {
Preconditions.checkArgument(newChild.next == null,
"The new child node has siblings.");
Preconditions.checkArgument(newChild.parent == null,
"The new child node already has a parent.");
// Copy over important information.
newChild.copyInformationFrom(child);
newChild.next = child.next;
newChild.parent = this;
if (child == first) {
first = newChild;
} else {
Node prev = getChildBefore(child);
prev.next = newChild;
}
if (child == last)
last = newChild;
child.next = null;
child.parent = null;
}
public void replaceChildAfter(Node prevChild, Node newChild) {
Preconditions.checkArgument(prevChild.parent == this,
"prev is not a child of this node.");
Preconditions.checkArgument(newChild.next == null,
"The new child node has siblings.");
Preconditions.checkArgument(newChild.parent == null,
"The new child node already has a parent.");
// Copy over important information.
newChild.copyInformationFrom(prevChild);
Node child = prevChild.next;
newChild.next = child.next;
newChild.parent = this;
prevChild.next = newChild;
if (child == last)
last = newChild;
child.next = null;
child.parent = null;
}
@VisibleForTesting
PropListItem lookupProperty(int propType) {
PropListItem x = propListHead;
while (x != null && propType != x.type) {
x = x.next;
}
return x;
}
/**
* Clone the properties from the provided node without copying
* the property object. The recieving node may not have any
* existing properties.
* @param other The node to clone properties from.
* @return this node.
*/
public Node clonePropsFrom(Node other) {
Preconditions.checkState(this.propListHead == null,
"Node has existing properties.");
this.propListHead = other.propListHead;
return this;
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> public void removeProp(int propType) {
PropListItem result = removeProp(propListHead, propType);
if (result != propListHead) {
propListHead = result;
}
}
/**
* @param item The item to inspect
* @param propType The property to look for
* @return The replacement list if the property was removed, or
* 'item' otherwise.
*/
private PropListItem removeProp(PropListItem item, int propType) {
if (item == null) {
return null;
} else if (item.type == propType) {
return item.next;
} else {
PropListItem result = removeProp(item.next, propType);
if (result != item.next) {
return new PropListItem(
item.type, item.intValue, item.objectValue, result);
} else {
return item;
}
}
}
public Object getProp(int propType) {
PropListItem item = lookupProperty(propType);
if (item == null) {
return null;
}
return item.objectValue;
}
public boolean getBooleanProp(int propType) {
return getIntProp(propType) != 0;
}
/**
* Returns the integer value for the property, or 0 if the property
* is not defined.
*/
public int getIntProp(int propType) {
PropListItem item = lookupProperty(propType);
if (item == null) {
return 0;
}
return item.intValue;
}
public int getExistingIntProp(int propType) {
PropListItem item = lookupProperty(propType);
if (item == null) {
Kit.codeBug();
}
return item.intValue;
}
public void putProp(int propType, Object value) {
removeProp(propType);
if (value != null) {
propListHead = new PropListItem(propType, value, propListHead);
}
}
public void putBooleanProp(int propType, boolean value) {
putIntProp(propType, value ? 1 : 0);
}
public void putIntProp(int propType, int value) {
removeProp(propType);
if (value != 0) {
prop
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>ListHead = new PropListItem(propType, value, propListHead);
}
}
// Gets all the property types, in sorted order.
private int[] getSortedPropTypes() {
int count = 0;
for (PropListItem x = propListHead; x != null; x = x.next) {
count++;
}
int[] keys = new int[count];
for (PropListItem x = propListHead; x != null; x = x.next) {
count--;
keys[count] = x.type;
}
Arrays.sort(keys);
return keys;
}
public int getLineno() {
return extractLineno(sourcePosition);
}
public int getCharno() {
return extractCharno(sourcePosition);
}
public int getSourcePosition() {
return sourcePosition;
}
/** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */
public double getDouble() throws UnsupportedOperationException {
if (this.getType() == Token.NUMBER) {
throw new IllegalStateException(
"Number node not created with Node.newNumber");
} else {
throw new UnsupportedOperationException(this + " is not a number node");
}
}
/** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */
public void setDouble(double s) throws UnsupportedOperationException {
if (this.getType() == Token.NUMBER) {
throw new IllegalStateException(
"Number node not created with Node.newNumber");
} else {
throw new UnsupportedOperationException(this + " is not a string node");
}
}
/** Can only be called when node has String context. */
public String getString() throws UnsupportedOperationException {
if (this.getType() == Token.STRING) {
throw new IllegalStateException(
"String node not created with Node.newString");
} else {
throw new UnsupportedOperationException(this + " is not a string node");
}
}
/** Can only be called when node has String context. */
public void setString(String s) throws UnsupportedOperationException {
if (this.getType() == Token.STRING) {
throw new IllegalStateException(
"String node not created with Node.newString");
} else {
throw new UnsupportedOperationException(this + " is not a string node");
}
}
@Override
public String toString()
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> {
return toString(true, true, true);
}
public String toString(
boolean printSource,
boolean printAnnotations,
boolean printType) {
if (Token.printTrees) {
StringBuilder sb = new StringBuilder();
toString(sb, printSource, printAnnotations, printType);
return sb.toString();
}
return String.valueOf(type);
}
private void toString(
StringBuilder sb,
boolean printSource,
boolean printAnnotations,
boolean printType) {
if (Token.printTrees) {
sb.append(Token.name(type));
if (this instanceof StringNode) {
sb.append(' ');
sb.append(getString());
} else if (type == Token.FUNCTION) {
sb.append(' ');
// In the case of JsDoc trees, the first child is often not a string
// which causes exceptions to be thrown when calling toString or
// toStringTree.
if (first == null || first.getType() != Token.NAME) {
sb.append("<invalid>");
} else {
sb.append(first.getString());
}
} else if (this instanceof ScriptOrFnNode) {
ScriptOrFnNode sof = (ScriptOrFnNode) this;
if (this instanceof FunctionNode) {
FunctionNode fn = (FunctionNode) this;
sb.append(' ');
sb.append(fn.getFunctionName());
}
if (printSource) {
sb.append(" [source name: ");
sb.append(sof.getSourceName());
sb.append("] [encoded source length: ");
sb.append(sof.getEncodedSourceEnd() - sof.getEncodedSourceStart());
sb.append("] [base line: ");
sb.append(sof.getBaseLineno());
sb.append("] [end line: ");
sb.append(sof.getEndLineno());
sb.append(']');
}
} else if (type == Token.NUMBER) {
sb.append(' ');
sb.append(getDouble());
}
if (printSource) {
int lineno = getLineno();
if (lineno != -1) {
sb.append(' ');
sb.append(lineno);
}
}
if (printAnnotations) {
int[] keys = getSortedPropTypes();
for (int i = 0;
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> i < keys.length; i++) {
int type = keys[i];
PropListItem x = lookupProperty(type);
sb.append(" [");
sb.append(propToString(type));
sb.append(": ");
String value;
switch (type) {
case TARGETBLOCK_PROP: // can't add this as it recurses
value = "target block property";
break;
case LOCAL_BLOCK_PROP: // can't add this as it is dull
value = "last local block";
break;
case ISNUMBER_PROP:
switch (x.intValue) {
case BOTH:
value = "both";
break;
case RIGHT:
value = "right";
break;
case LEFT:
value = "left";
break;
default:
throw Kit.codeBug();
}
break;
case SPECIALCALL_PROP:
switch (x.intValue) {
case SPECIALCALL_EVAL:
value = "eval";
break;
case SPECIALCALL_WITH:
value = "with";
break;
default:
// NON_SPECIALCALL should not be stored
throw Kit.codeBug();
}
break;
default:
Object obj = x.objectValue;
if (obj != null) {
value = obj.toString();
} else {
value = String.valueOf(x.intValue);
}
break;
}
sb.append(value);
sb.append(']');
}
}
if (printType) {
if (jsType != null) {
String jsTypeString = jsType.toString();
if (jsTypeString != null) {
sb.append(" : ");
sb.append(jsTypeString);
}
}
}
}
}
public String toStringTree() {
return toStringTreeImpl();
}
private String toStringTreeImpl() {
try {
StringBuilder s = new StringBuilder();
appendStringTree(s);
return s.toString();
} catch (IOException e) {
throw new RuntimeException("Should not happen\n" + e);
}
}
public void appendStringTree(Appendable appendable) throws IOException {
toStringTreeHelper(this, 0, appendable);
}
private static void toStringTreeHelper(Node n, int level, Appendable sb)
throws IOException {
if (Token.
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>printTrees) {
for (int i = 0; i != level; ++i) {
sb.append(" ");
}
sb.append(n.toString());
sb.append('\n');
for (Node cursor = n.getFirstChild();
cursor != null;
cursor = cursor.getNext()) {
toStringTreeHelper(cursor, level + 1, sb);
}
}
}
int type; // type of the node; Token.NAME for example
Node next; // next sibling
private Node first; // first element of a linked list of children
private Node last; // last element of a linked list of children
/**
* Linked list of properties. Since vast majority of nodes would have
* no more then 2 properties, linked list saves memory and provides
* fast lookup. If this does not holds, propListHead can be replaced
* by UintMap.
*/
private PropListItem propListHead;
/**
* COLUMN_BITS represents how many of the lower-order bits of
* sourcePosition are reserved for storing the column number.
* Bits above these store the line number.
* This gives us decent position information for everything except
* files already passed through a minimizer, where lines might
* be longer than 4096 characters.
*/
public static final int COLUMN_BITS = 12;
/**
* MAX_COLUMN_NUMBER represents the maximum column number that can
* be represented. JSCompiler's modifications to Rhino cause all
* tokens located beyond the maximum column to MAX_COLUMN_NUMBER.
*/
public static final int MAX_COLUMN_NUMBER = (1 << COLUMN_BITS) - 1;
/**
* COLUMN_MASK stores a value where bits storing the column number
* are set, and bits storing the line are not set. It's handy for
* separating column number from line number.
*/
public static final int COLUMN_MASK = MAX_COLUMN_NUMBER;
/**
* Source position of this node. The position is encoded with the
* column number in the low 12 bits of the integer, and the line
* number in the rest. Create some handy constants so we can change this
* size if we want.
*/
private int sourcePosition;
private JSType jsType;
private Node parent;
//================================================================
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> Token.ASSIGN_ADD: return "assign_add";
case Token.ASSIGN_SUB: return "assign_sub";
case Token.ASSIGN_MUL: return "assign_mul";
case Token.ASSIGN_DIV: return "assign_div";
case Token.ASSIGN_MOD: return "assign_mod";
case Token.HOOK: return "hook";
case Token.COLON: return "colon";
case Token.OR: return "or";
case Token.AND: return "and";
case Token.INC: return "inc";
case Token.DEC: return "dec";
case Token.DOT: return "dot";
case Token.FUNCTION: return "function";
case Token.EXPORT: return "export";
case Token.IMPORT: return "import";
case Token.IF: return "if";
case Token.ELSE: return "else";
case Token.SWITCH: return "switch";
case Token.CASE: return "case";
case Token.DEFAULT: return "default";
case Token.WHILE: return "while";
case Token.DO: return "do";
case Token.FOR: return "for";
case Token.BREAK: return "break";
case Token.CONTINUE: return "continue";
case Token.VAR: return "var";
case Token.WITH: return "with";
case Token.CATCH: return "catch";
case Token.FINALLY: return "finally";
case Token.RESERVED: return "reserved";
case Token.NOT: return "not";
case Token.VOID: return "void";
case Token.BLOCK: return "block";
case Token.ARRAYLIT: return "arraylit";
case Token.OBJECTLIT: return "objectlit";
case Token.LABEL: return "label";
case Token.TARGET: return "target";
case Token.LOOP: return "loop";
case Token.EXPR_VOID: return "expr_void";
case Token.EXPR_RESULT: return "expr_result";
case Token.JSR: return "jsr";
case Token.SCRIPT: return "script";
case Token.EMPTY: return "empty";
case Token.GET
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>_REF: return "get_ref";
case Token.REF_SPECIAL: return "ref_special";
}
return "<unknown="+token+">";
}
/** Returns true if this node is equivalent semantically to another */
public boolean isEquivalentTo(Node node) {
return isEquivalentTo(node, false, true);
}
/**
* Returns true if this node is equivalent semantically to another and
* the types are equivalent.
*/
public boolean isEquivalentToTyped(Node node) {
return isEquivalentTo(node, true, true);
}
/**
* @param compareJsType Whether to compare the JSTypes of the nodes.
* @param recurse Whether to compare the children of the current node, if
* not only the the count of the children are compared.
* @return Whether this node is equivalent semantically to the provided node.
*/
boolean isEquivalentTo(Node node, boolean compareJsType, boolean recurse) {
if (type != node.getType()
|| getChildCount() != node.getChildCount()
|| getNodeClass(this) != getNodeClass(node)) {
return false;
}
if (compareJsType && !JSType.isEquivalent(jsType, node.getJSType())) {
return false;
}
if (type == Token.ARRAYLIT) {
try {
int[] indices1 = (int[]) getProp(Node.SKIP_INDEXES_PROP);
int[] indices2 = (int[]) node.getProp(Node.SKIP_INDEXES_PROP);
if (indices1 == null) {
if (indices2 != null) {
return false;
}
} else if (indices2 == null) {
return false;
} else if (indices1.length != indices2.length) {
return false;
} else {
for (int i = 0; i < indices1.length; i++) {
if (indices1[i] != indices2[i]) {
return false;
}
}
}
} catch (Exception e) {
return false;
}
} else if (type == Token.INC || type == Token.DEC) {
int post1 = this.getIntProp(INCRDECR_PROP);
int post2 = node.getIntProp(INCRDECR
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>_PROP);
if (post1 != post2) {
return false;
}
} else if (type == Token.STRING) {
int quoted1 = this.getIntProp(QUOTED_PROP);
int quoted2 = node.getIntProp(QUOTED_PROP);
if (quoted1 != quoted2) {
return false;
}
}
if (recurse) {
Node n, n2;
for (n = first, n2 = node.first;
n != null;
n = n.next, n2 = n2.next) {
if (!n.isEquivalentTo(n2, compareJsType, true)) {
return false;
}
}
}
return true;
}
public boolean hasSideEffects() {
switch (type) {
case Token.EXPR_VOID:
case Token.COMMA:
if (last != null)
return last.hasSideEffects();
else
return true;
case Token.HOOK:
if (first == null || first.next == null || first.next.next == null) {
Kit.codeBug();
}
return first.next.hasSideEffects() && first.next.next.hasSideEffects();
case Token.ERROR: // Avoid cascaded error messages
case Token.EXPR_RESULT:
case Token.ASSIGN:
case Token.ASSIGN_ADD:
case Token.ASSIGN_SUB:
case Token.ASSIGN_MUL:
case Token.ASSIGN_DIV:
case Token.ASSIGN_MOD:
case Token.ASSIGN_BITOR:
case Token.ASSIGN_BITXOR:
case Token.ASSIGN_BITAND:
case Token.ASSIGN_LSH:
case Token.ASSIGN_RSH:
case Token.ASSIGN_URSH:
case Token.ENTERWITH:
case Token.LEAVEWITH:
case Token.RETURN:
case Token.GOTO:
case Token.IFEQ:
case Token.IFNE:
case Token.NEW:
case Token.DELPROP:
case Token.SETNAME:
case Token.SETPROP:
case Token.SETELEM:
case Token.CALL:
case Token.THROW:
case Token.RETHROW:
case Token.SETVAR:
case Token.CATCH_SCOPE:
case Token.RETURN_RESULT:
case Token.SET_
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>REF:
case Token.DEL_REF:
case Token.REF_CALL:
case Token.TRY:
case Token.SEMI:
case Token.INC:
case Token.DEC:
case Token.EXPORT:
case Token.IMPORT:
case Token.IF:
case Token.ELSE:
case Token.SWITCH:
case Token.WHILE:
case Token.DO:
case Token.FOR:
case Token.BREAK:
case Token.CONTINUE:
case Token.VAR:
case Token.CONST:
case Token.WITH:
case Token.CATCH:
case Token.FINALLY:
case Token.BLOCK:
case Token.LABEL:
case Token.TARGET:
case Token.LOOP:
case Token.JSR:
case Token.SETPROP_OP:
case Token.SETELEM_OP:
case Token.LOCAL_BLOCK:
case Token.SET_REF_OP:
return true;
default:
return false;
}
}
/**
* This function takes a set of GETPROP nodes and produces a string that is
* each property separated by dots. If the node ultimately under the left
* sub-tree is not a simple name, this is not a valid qualified name.
*
* @return a null if this is not a qualified name, or a dot-separated string
* of the name and properties.
*/
public String getQualifiedName() {
if (type == Token.NAME) {
return getString();
} else if (type == Token.GETPROP) {
String left = getFirstChild().getQualifiedName();
if (left == null) {
return null;
}
return left + "." + getLastChild().getString();
} else if (type == Token.THIS) {
return "this";
} else {
return null;
}
}
/**
* Returns whether a node corresponds to a simple or a qualified name, such as
* <code>x</code> or <code>a.b.c</code> or <code>this.a</code>.
*/
public boolean isQualifiedName() {
switch (getType()) {
case Token.NAME:
case Token.THIS:
return true;
case Token.GETPROP:
return getFirstChild().isQualifiedName();
default:
return false;
}
}
/**
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>.com (John Lenz)
*/
public static class SideEffectFlags {
private int value = Node.SIDE_EFFECTS_ALL;
public SideEffectFlags() {
}
public SideEffectFlags(int value) {
this.value = value;
}
public int valueOf() {
return value;
}
/** All side-effect occur and the returned results are non-local. */
public void setAllFlags() {
value = Node.SIDE_EFFECTS_ALL;
}
/** No side-effects occur and the returned results are local. */
public void clearAllFlags() {
value = Node.NO_SIDE_EFFECTS | Node.FLAG_LOCAL_RESULTS;
}
public boolean areAllFlagsSet() {
return value == Node.SIDE_EFFECTS_ALL;
}
/**
* Preserve the return result flag, but clear the others:
* no global state change, no throws, no this change, no arguments change
*/
public void clearSideEffectFlags() {
value |= Node.NO_SIDE_EFFECTS;
}
public void setMutatesGlobalState() {
// Modify global means everything must be assumed to be modified.
removeFlag(Node.FLAG_GLOBAL_STATE_UNMODIFIED);
removeFlag(Node.FLAG_ARGUMENTS_UNMODIFIED);
removeFlag(Node.FLAG_THIS_UNMODIFIED);
}
public void setThrows() {
removeFlag(Node.FLAG_NO_THROWS);
}
public void setMutatesThis() {
removeFlag(Node.FLAG_THIS_UNMODIFIED);
}
public void setMutatesArguments() {
removeFlag(Node.FLAG_ARGUMENTS_UNMODIFIED);
}
public void setReturnsTainted() {
removeFlag(Node.FLAG_LOCAL_RESULTS);
}
private void removeFlag(int flag) {
value &= ~flag;
}
}
/**
* @return Whether the only side-effect is "modifies this"
*/
public boolean isOnlyModifiesThisCall() {
return areBitFlagsSet(
getSideEffectFlags() & Node.NO_SIDE_EFFECTS,
Node.FLAG_GLOBAL_STATE_UNMODIFIED
| Node.FLAG_ARGUMENTS_UNMODIFIED
| Node.FLAG_NO_TH
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>ROWS);
}
/**
* Returns true if this node is a function or constructor call that
* has no side effects.
*/
public boolean isNoSideEffectsCall() {
return areBitFlagsSet(getSideEffectFlags(), NO_SIDE_EFFECTS);
}
/**
* Returns true if this node is a function or constructor call that
* returns a primitive or a local object (an object that has no other
* references).
*/
public boolean isLocalResultCall() {
return areBitFlagsSet(getSideEffectFlags(), FLAG_LOCAL_RESULTS);
}
/**
* returns true if all the flags are set in value.
*/
private boolean areBitFlagsSet(int value, int flags) {
return (value & flags) == flags;
}
/**
* This should only be called for STRING nodes children of OBJECTLIT.
*/
public boolean isQuotedString() {
return false;
}
/**
* This should only be called for STRING nodes children of OBJECTLIT.
*/
public void setQuotedString() {
Kit.codeBug();
}
static class NodeMismatch {
final Node nodeA;
final Node nodeB;
NodeMismatch(Node nodeA, Node nodeB) {
this.nodeA = nodeA;
this.nodeB = nodeB;
}
@Override
public boolean equals(Object object) {
if (object instanceof NodeMismatch) {
NodeMismatch that = (NodeMismatch) object;
return that.nodeA.equals(this.nodeA) && that.nodeB.equals(this.nodeB);
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(nodeA, nodeB);
}
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>() {
return false;
}
@Override
public TernaryValue testForEquality(JSType that) {
TernaryValue result = super.testForEquality(that);
if (result != null) {
return result;
}
if (that.isUnknownType() || that.isSubtype(
getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN))) {
return UNKNOWN;
}
return FALSE;
}
@Override
public boolean isNumberValueType() {
return true;
}
@Override
public boolean matchesNumberContext() {
return true;
}
@Override
public boolean matchesStringContext() {
return true;
}
@Override
public boolean matchesObjectContext() {
// TODO(user): Revisit this for ES4, which is stricter.
return true;
}
@Override
public String toString() {
return getDisplayName();
}
@Override
public String getDisplayName() {
return "number";
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
return BooleanLiteralSet.BOTH;
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseNumberType();
}
@Override
public JSType autoboxesTo() {
return getNativeType(JSTypeNative.NUMBER_OBJECT_TYPE);
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
}
InstanceObjectType(JSTypeRegistry registry, FunctionType constructor,
boolean isNativeType) {
super(registry, null, null, isNativeType);
Preconditions.checkNotNull(constructor);
this.constructor = constructor;
}
@Override
public String getReferenceName() {
return getConstructor().getReferenceName();
}
@Override
public boolean hasReferenceName() {
return getConstructor().hasReferenceName();
}
@Override
public ObjectType getImplicitPrototype() {
return getConstructor().getPrototype();
}
@Override
public FunctionType getConstructor() {
return constructor;
}
@Override
boolean defineProperty(String name, JSType type, boolean inferred,
boolean inExterns, Node propertyNode) {
ObjectType proto = getImplicitPrototype();
if (proto != null && proto.hasOwnDeclaredProperty(name)) {
return false;
}
return super.defineProperty(name, type, inferred, inExterns, propertyNode);
}
@Override
public String toString() {
if (constructor.hasReferenceName()) {
return constructor.getReferenceName();
} else {
return super.toString();
}
}
@Override
boolean isTheObjectType() {
return getConstructor().isNative() && "Object".equals(getReferenceName());
}
@Override
public boolean isInstanceType() {
return true;
}
@Override
public boolean isArrayType() {
return getConstructor().isNative() && "Array".equals(getReferenceName());
}
@Override
public boolean isStringObjectType() {
return getConstructor().isNative() && "String".equals(getReferenceName());
}
@Override
public boolean isBooleanObjectType() {
return getConstructor().isNative() && "Boolean".equals(getReferenceName());
}
@Override
public boolean isNumberObjectType() {
return getConstructor().isNative() && "Number".equals(getReferenceName());
}
@Override
public boolean isDateType() {
return getConstructor().isNative() && "Date".equals(getReferenceName());
}
@Override
public boolean isRegexpType() {
return getConstructor().isNative() && "RegExp".equals(getReferenceName());
}
@Override
public boolean isNominalType() {
return hasReferenceName();
}
@Override
public boolean isEquivalentTo(JSType that)
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>(JSTypeRegistry registry) {
super(registry);
}
@Override
public boolean isNullType() {
return true;
}
@Override
public boolean isNullable() {
return true;
}
@Override
public boolean matchesNumberContext() {
return true;
}
@Override
public boolean matchesObjectContext() {
return false;
}
@Override
public boolean matchesStringContext() {
return true;
}
@Override
public JSType restrictByNotNullOrUndefined() {
return registry.getNativeType(JSTypeNative.NO_TYPE);
}
@Override
public TernaryValue testForEquality(JSType that) {
TernaryValue result = super.testForEquality(that);
if (result != null) {
return result;
}
if (that.isNullType() || that.isVoidType()) {
return TRUE;
}
if (that.isUnknownType() || that.isNullable()) {
return UNKNOWN;
}
return FALSE;
}
@Override
public String toString() {
return getDisplayName();
}
@Override
public String getDisplayName() {
return "null";
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
return BooleanLiteralSet.FALSE;
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseNullType();
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>REFERENCE_ERROR_TYPE));
register(getNativeType(JSTypeNative.SYNTAX_ERROR_TYPE));
register(getNativeType(JSTypeNative.REGEXP_TYPE));
register(getNativeType(JSTypeNative.STRING_OBJECT_TYPE));
register(getNativeType(JSTypeNative.STRING_TYPE));
register(getNativeType(JSTypeNative.VOID_TYPE));
register(getNativeType(JSTypeNative.VOID_TYPE), "Undefined");
register(getNativeType(JSTypeNative.VOID_TYPE), "void");
register(getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE), "Function");
}
private void register(JSType type) {
register(type, type.toString());
}
private void register(JSType type, String name) {
namesToTypes.put(name, type);
// Add all the namespaces in which this name lives.
while (name.indexOf('.') > 0) {
name = name.substring(0, name.lastIndexOf('.'));
namespaces.add(name);
}
}
private void registerNativeType(JSTypeNative typeId, JSType type) {
nativeTypes[typeId.ordinal()] = type;
}
/**
* Tells the type system that {@code owner} may have a property named
* {@code propertyName}. This allows the registry to keep track of what
* types a property is defined upon.
*
* This is NOT the same as saying that {@code owner} must have a property
* named type. ObjectType#hasProperty attempts to minimize false positives
* ("if we're not sure, then don't type check this property"). The type
* registry, on the other hand, should attempt to minimize false negatives
* ("if this property is assigned anywhere in the program, it must
* show up in the type registry").
*/
public void registerPropertyOnType(String propertyName, JSType type) {
UnionTypeBuilder typeSet = typesIndexedByProperty.get(propertyName);
if (typeSet == null) {
typeSet = new UnionTypeBuilder(this);
typesIndexedByProperty.put(propertyName, typeSet);
}
typeSet.addAlternate(type);
addReferenceTypeIndexedByProperty(propertyName, type);
// Clear cached values that depend on typesIndexedByProperty.
greatest
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>SubtypeByProperty.remove(propertyName);
}
private void addReferenceTypeIndexedByProperty(
String propertyName, JSType type) {
if (type instanceof ObjectType && ((ObjectType) type).hasReferenceName()) {
Map<String, ObjectType> typeSet =
eachRefTypeIndexedByProperty.get(propertyName);
if (typeSet == null) {
typeSet = Maps.newHashMap();
eachRefTypeIndexedByProperty.put(propertyName, typeSet);
}
ObjectType objType = (ObjectType) type;
typeSet.put(objType.getReferenceName(), objType);
} else if (type instanceof NamedType) {
addReferenceTypeIndexedByProperty(
propertyName, ((NamedType) type).getReferencedType());
} else if (type instanceof UnionType) {
for (JSType alternate : ((UnionType) type).getAlternates()) {
addReferenceTypeIndexedByProperty(propertyName, alternate);
}
}
}
/**
* Gets the greatest subtype of the {@code type} that has a property
* {@code propertyName} defined on it.
*/
public JSType getGreatestSubtypeWithProperty(
JSType type, String propertyName) {
if (greatestSubtypeByProperty.containsKey(propertyName)) {
return greatestSubtypeByProperty.get(propertyName)
.getGreatestSubtype(type);
}
if (typesIndexedByProperty.containsKey(propertyName)) {
JSType built = typesIndexedByProperty.get(propertyName).build();
greatestSubtypeByProperty.put(propertyName, built);
return built.getGreatestSubtype(type);
}
return getNativeType(NO_TYPE);
}
/**
* Returns whether the given property can possibly be set on the given type.
*/
public boolean canPropertyBeDefined(JSType type, String propertyName) {
if (typesIndexedByProperty.containsKey(propertyName)) {
for (JSType alt :
typesIndexedByProperty.get(propertyName).getAlternates()) {
if (!alt.getGreatestSubtype(type).isEmptyType()) {
return true;
}
}
}
return false;
}
/**
* Returns each type that has a property {@code propertyName} defined on it.
*
* Like most types in our type system, the collection of types returned
* will be collapsed. This means that if a type is defined on
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
* {@code Object} and on {@code Array}, it would be reasonable for this
* method to return either {@code [Object, Array]} or just {@code [Object]}.
*/
public Iterable<JSType> getTypesWithProperty(String propertyName) {
if (typesIndexedByProperty.containsKey(propertyName)) {
return typesIndexedByProperty.get(propertyName).getAlternates();
} else {
return ImmutableList.of();
}
}
/**
* Returns each reference type that has a property {@code propertyName}
* defined on it.
*
* Unlike most types in our type system, the collection of types returned
* will not be collapsed. This means that if a type is defined on
* {@code Object} and on {@code Array}, this method must return
* {@code [Object, Array]}. It would not be correct to collapse them to
* {@code [Object]}.
*/
public Iterable<ObjectType> getEachReferenceTypeWithProperty(
String propertyName) {
if (eachRefTypeIndexedByProperty.containsKey(propertyName)) {
return eachRefTypeIndexedByProperty.get(propertyName).values();
} else {
return ImmutableList.of();
}
}
/**
* Increments the current generation. Clients must call this in order to
* move to the next generation of type resolution, allowing types to attempt
* resolution again.
*/
public void incrementGeneration() {
for (NamedType type : resolvedNamedTypes.values()) {
type.clearResolved();
}
unresolvedNamedTypes.putAll(resolvedNamedTypes);
resolvedNamedTypes.clear();
}
boolean isLastGeneration() {
return lastGeneration;
}
/**
* Sets whether this is the last generation. In the last generation,
* {@link NamedType} warns about unresolved types.
*/
public void setLastGeneration(boolean lastGeneration) {
this.lastGeneration = lastGeneration;
}
/**
* Tells the type system that {@code type} implements interface {@code
* InterfaceInstance}.
* {@code inter} must be an ObjectType for the instance of the interface as it
* could be a named type and not yet have the constructor.
*/
void registerTypeImplementingInterface(
FunctionType type, ObjectType interfaceInstance) {
interfaceToImplementors.put(interfaceInstance.getReferenceName(), type);
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>Type getNativeType(JSTypeNative typeId) {
return nativeTypes[typeId.ordinal()];
}
public ObjectType getNativeObjectType(JSTypeNative typeId) {
return (ObjectType) getNativeType(typeId);
}
public FunctionType getNativeFunctionType(JSTypeNative typeId) {
return (FunctionType) getNativeType(typeId);
}
/**
* Looks up a type by name. To allow for forward references to types, an
* unrecognized string has to be bound to a NamedType object that will be
* resolved later.
*
* @param scope A scope for doing type name resolution.
* @param jsTypeName The name string.
* @param sourceName The name of the source file where this reference appears.
* @param lineno The line number of the reference.
* @return a NamedType if the string argument is not one of the known types,
* otherwise the corresponding JSType object.
*/
public JSType getType(StaticScope<JSType> scope, String jsTypeName,
String sourceName, int lineno, int charno) {
JSType type = getType(jsTypeName);
if (type == null) {
// TODO(user): Each instance should support named type creation using
// interning.
NamedType namedType =
new NamedType(this, jsTypeName, sourceName, lineno, charno);
unresolvedNamedTypes.put(scope, namedType);
type = namedType;
}
return type;
}
/**
* Flushes out the current resolved and unresovled Named Types from
* the type registry. This is intended to be used ONLY before a
* compile is run.
*/
public void clearNamedTypes() {
resolvedNamedTypes.clear();
unresolvedNamedTypes.clear();
}
/**
* Resolve all the unresolved types in the given scope.
*/
public void resolveTypesInScope(StaticScope<JSType> scope) {
for (NamedType type : unresolvedNamedTypes.get(scope)) {
type.resolve(reporter, scope);
}
resolvedNamedTypes.putAll(scope, unresolvedNamedTypes.removeAll(scope));
if (scope != null && scope.getParentScope() == null) {
// By default, the global "this" type is just an anonymous object.
// If the user
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> has defined a Window type, make the Window the
// implicit prototype of "this".
PrototypeObjectType globalThis = (PrototypeObjectType) getNativeType(
JSTypeNative.GLOBAL_THIS);
JSType windowType = getType("Window");
if (globalThis.isUnknownType()) {
ObjectType windowObjType = ObjectType.cast(windowType);
if (windowObjType != null) {
globalThis.setImplicitPrototype(windowObjType);
} else {
globalThis.setImplicitPrototype(
getNativeObjectType(JSTypeNative.OBJECT_TYPE));
}
}
}
}
/**
* Creates a type representing optional values of the given type.
* @return the union of the type and the void type
*/
public JSType createOptionalType(JSType type) {
if (type instanceof UnknownType || type.isAllType()) {
return type;
} else {
return createUnionType(type, getNativeType(JSTypeNative.VOID_TYPE));
}
}
/**
* Creates a type representing nullable values of the given type.
* @return the union of the type and the Null type
*/
public JSType createDefaultObjectUnion(JSType type) {
return shouldTolerateUndefinedValues()
? createOptionalNullableType(type)
: createNullableType(type);
}
/**
* Creates a type representing nullable values of the given type.
* @return the union of the type and the Null type
*/
public JSType createNullableType(JSType type) {
return createUnionType(type, getNativeType(JSTypeNative.NULL_TYPE));
}
/**
* Creates a nullabel and undefine-able value of the given type.
* @return The union of the type and null and undefined.
*/
public JSType createOptionalNullableType(JSType type) {
return createUnionType(type, getNativeType(JSTypeNative.VOID_TYPE),
getNativeType(JSTypeNative.NULL_TYPE));
}
/**
* Creates a union type whose variants are the arguments.
*/
public JSType createUnionType(JSType... variants) {
UnionTypeBuilder builder = new UnionTypeBuilder(this);
for (JSType type : variants) {
builder.addAlternate(type);
}
return builder.build();
}
/**
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> type or {@code null} to indicate
* that the return type is unknown.
*/
public FunctionType createFunctionType(
JSType returnType, Node parameters) {
return new FunctionBuilder(this)
.withParamsNode(parameters)
.withReturnType(returnType)
.build();
}
/**
* Creates a function type which can act as a constructor.
* @param returnType the function's return type
* @param lastVarArgs whether the last parameter type should be considered as
* an extensible var_args parameter
* @param parameterTypes the parameters' types
*/
public FunctionType createConstructorType(JSType returnType,
boolean lastVarArgs, JSType... parameterTypes) {
if (lastVarArgs) {
return createConstructorTypeWithVarArgs(returnType, parameterTypes);
} else {
return createConstructorType(returnType, parameterTypes);
}
}
/**
* Create an object type.
*/
public ObjectType createObjectType(ObjectType implicitPrototype) {
return createObjectType(null, null, implicitPrototype);
}
/**
* Creates a record type.
*/
public RecordType createRecordType(Map<String, RecordProperty> properties) {
return new RecordType(this, properties);
}
/**
* Create an object type.
*/
public ObjectType createObjectType(String name, Node n,
ObjectType implicitPrototype) {
return new PrototypeObjectType(this, name, implicitPrototype);
}
/**
* Create an anonymous object type.
*/
public ObjectType createAnonymousObjectType() {
PrototypeObjectType type =
new PrototypeObjectType(this, null, null);
type.setPrettyPrint(true);
return type;
}
/**
* Creates a constructor function type.
* @param name the function's name or {@code null} to indicate that the
* function is anonymous.
* @param source the node defining this function. Its type
* ({@link Node#getType()}) must be {@link Token#FUNCTION}.
* @param parameters the function's parameters or {@code null}
* to indicate that the parameter types are unknown.
* @param returnType the function's return type or {@code null} to indicate
* that the return type is unknown.
*/
public FunctionType createConstructorType(String name, Node source,
Node parameters, JSType returnType) {
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>Name, scope));
}
} else {
JSType type = createFromTypeNodesInternal(
arg, sourceName, scope);
if (arg.getType() == Token.EQUALS) {
boolean addSuccess = paramBuilder.addOptionalParams(type);
if (!addSuccess) {
reporter.warning(
ScriptRuntime.getMessage0("msg.jsdoc.function.varargs"),
sourceName, arg.getLineno(), "", arg.getCharno());
}
} else {
paramBuilder.addRequiredParams(type);
}
}
}
current = current.getNext();
}
JSType returnType =
createFromTypeNodesInternal(current, sourceName, scope);
return new FunctionBuilder(this)
.withParams(paramBuilder)
.withReturnType(returnType)
.withTypeOfThis(thisType)
.setIsConstructor(isConstructor)
.build();
}
throw new IllegalStateException(
"Unexpected node in type expression: " + n.toString());
}
/**
* Creates a RecordType from the nodes representing said record type.
* @param n The node with type info.
* @param sourceName The source file name.
* @param scope A scope for doing type name lookups.
*/
private JSType createRecordTypeFromNodes(Node n, String sourceName,
StaticScope<JSType> scope) {
RecordTypeBuilder builder = new RecordTypeBuilder(this);
// For each of the fields in the record type.
for (Node fieldTypeNode = n.getFirstChild();
fieldTypeNode != null;
fieldTypeNode = fieldTypeNode.getNext()) {
// Get the property's name.
Node fieldNameNode = fieldTypeNode;
boolean hasType = false;
if (fieldTypeNode.getType() == Token.COLON) {
fieldNameNode = fieldTypeNode.getFirstChild();
hasType = true;
}
String fieldName = fieldNameNode.getString();
// TODO(user): Move this into the lexer/parser.
// Remove the string literal characters around a field name,
// if any.
if (fieldName.startsWith("'") || fieldName.startsWith("\"")) {
fieldName = fieldName.substring(1, fieldName.length() - 1);
}
// Get the property's type.
JSType fieldType = null;
if (hasType) {
// We have a declared
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> this to an ObjectType, or returns null if this is not an ObjectType.
*
* Does not change the underlying JS type. If you want to simulate JS
* autoboxing or dereferencing, you should use autoboxesTo() or dereference().
* Those methods may change the underlying JS type.
*/
public ObjectType toObjectType() {
return this instanceof ObjectType ? (ObjectType) this : null;
}
/**
* Dereference a type for property access.
*
* Autoboxes the type, filters null/undefined, and returns the result
* iff it's an object.
*/
public final ObjectType dereference() {
JSType restricted = restrictByNotNullOrUndefined();
JSType autobox = restricted.autoboxesTo();
return ObjectType.cast(autobox == null ? restricted : autobox);
}
/**
* Tests whether {@code this} and {@code that} are meaningfully
* comparable. By meaningfully, we mean compatible types that do not lead
* to step 22 of the definition of the Abstract Equality Comparison
* Algorithm (11.9.3, page 55–56) of the ECMA-262 specification.<p>
*/
public final boolean canTestForEqualityWith(JSType that) {
return this.testForEquality(that).equals(UNKNOWN);
}
/**
* Compares {@code this} and {@code that}.
* @return <ul>
* <li>{@link TernaryValue#TRUE} if the comparison of values of
* {@code this} type and {@code that} always succeed (such as
* {@code undefined} compared to {@code null})</li>
* <li>{@link TernaryValue#FALSE} if the comparison of values of
* {@code this} type and {@code that} always fails (such as
* {@code undefined} compared to {@code number})</li>
* <li>{@link TernaryValue#UNKNOWN} if the comparison can succeed or
* fail depending on the concrete values</li>
* </ul>
*/
public TernaryValue testForEquality(JSType that) {
return testForEqualityHelper(this, that);
}
TernaryValue testForEqualityHelper(JSType aType, JSType bType) {
if (bType.is
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>AllType() || bType.isUnknownType() ||
bType.isNoResolvedType() ||
aType.isAllType() || aType.isUnknownType() ||
aType.isNoResolvedType()) {
return UNKNOWN;
}
boolean aIsEmpty = aType.isEmptyType();
boolean bIsEmpty = bType.isEmptyType();
if (aIsEmpty || bIsEmpty) {
if (aIsEmpty && bIsEmpty) {
return TernaryValue.TRUE;
} else {
return UNKNOWN;
}
}
if (aType.isFunctionType() || bType.isFunctionType()) {
JSType otherType = aType.isFunctionType() ? bType : aType;
// In theory, functions are comparable to anything except
// null/undefined. For example, on FF3:
// function() {} == 'function () {\n}'
// In practice, how a function serializes to a string is
// implementation-dependent, so it does not really make sense to test
// for equality with a string.
JSType meet = otherType.getGreatestSubtype(
getNativeType(JSTypeNative.OBJECT_TYPE));
if (meet.isNoType() || meet.isNoObjectType()) {
return TernaryValue.FALSE;
} else {
return TernaryValue.UNKNOWN;
}
}
if (bType.isEnumElementType() || bType.isUnionType()) {
return bType.testForEquality(aType);
}
return null;
}
/**
* Tests whether {@code this} and {@code that} are meaningfully
* comparable using shallow comparison. By meaningfully, we mean compatible
* types that are not rejected by step 1 of the definition of the Strict
* Equality Comparison Algorithm (11.9.6, page 56–57) of the
* ECMA-262 specification.<p>
*/
public final boolean canTestForShallowEqualityWith(JSType that) {
return this.isSubtype(that) || that.isSubtype(this);
}
/**
* Tests whether this type is nullable.
*/
public boolean isNullable() {
return this.isSubtype(getNativeType(JSTypeNative.NULL_TYPE));
}
/**
* Gets the least supertype of {@code this
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> used as a helper for common
* getGreatestSubtype implementations.
*/
static JSType getGreatestSubtype(JSType thisType, JSType thatType) {
if (thisType.isEquivalentTo(thatType)) {
return thisType;
} else if (thisType.isUnknownType() || thatType.isUnknownType()) {
// The greatest subtype with any unknown type is the universal
// unknown type, unless the two types are equal.
return thisType.isEquivalentTo(thatType) ? thisType :
thisType.getNativeType(JSTypeNative.UNKNOWN_TYPE);
} else if (thisType.isSubtype(thatType)) {
return filterNoResolvedType(thisType);
} else if (thatType.isSubtype(thisType)) {
return filterNoResolvedType(thatType);
} else if (thisType.isUnionType()) {
return ((UnionType) thisType).meet(thatType);
} else if (thatType.isUnionType()) {
return ((UnionType) thatType).meet(thisType);
} else if (thisType.isObject() && thatType.isObject()) {
return thisType.getNativeType(JSTypeNative.NO_OBJECT_TYPE);
}
return thisType.getNativeType(JSTypeNative.NO_TYPE);
}
/**
* When computing infimums, we may get a situation like
* inf(Type1, Type2)
* where both types are unresolved, so they're technically
* subtypes of one another.
*
* If this happens, filter them down to NoResolvedType.
*/
static JSType filterNoResolvedType(JSType type) {
if (type.isNoResolvedType()) {
// inf(UnresolvedType1, UnresolvedType2) needs to resolve
// to the base unresolved type, so that the relation is symmetric.
return type.getNativeType(JSTypeNative.NO_RESOLVED_TYPE);
} else if (type instanceof UnionType) {
UnionType unionType = (UnionType) type;
boolean needsFiltering = false;
for (JSType alt : unionType.getAlternates()) {
if (alt.isNoResolvedType()) {
needsFiltering = true;
break;
}
}
if (needsFiltering) {
Union
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>TypeBuilder builder = new UnionTypeBuilder(type.registry);
for (JSType alt : unionType.getAlternates()) {
if (!alt.isNoResolvedType()) {
builder.addAlternate(alt);
}
}
return builder.build();
}
}
return type;
}
/**
* Computes the restricted type of this type knowing that the
* {@code ToBoolean} predicate has a specific value. For more information
* about the {@code ToBoolean} predicate, see
* {@link #getPossibleToBooleanOutcomes}.
*
* @param outcome the value of the {@code ToBoolean} predicate
*
* @return the restricted type, or the Any Type if the underlying type could
* not have yielded this ToBoolean value
*
* TODO(user): Move this method to the SemanticRAI and use the visit
* method of types to get the restricted type.
*/
public JSType getRestrictedTypeGivenToBooleanOutcome(boolean outcome) {
BooleanLiteralSet literals = getPossibleToBooleanOutcomes();
if (literals.contains(outcome)) {
return this;
} else {
return getNativeType(JSTypeNative.NO_TYPE);
}
}
/**
* Computes the set of possible outcomes of the {@code ToBoolean} predicate
* for this type. The {@code ToBoolean} predicate is defined by the ECMA-262
* standard, 3<sup>rd</sup> edition. Its behavior for simple types can be
* summarized by the following table:
* <table>
* <tr><th>type</th><th>result</th></tr>
* <tr><td>{@code undefined}</td><td>{false}</td></tr>
* <tr><td>{@code null}</td><td>{false}</td></tr>
* <tr><td>{@code boolean}</td><td>{true, false}</td></tr>
* <tr><td>{@code number}</td><td>{true, false}</td></tr>
* <tr><td>{@code string}</td><td>{true, false}</td></tr>
* <tr><td>{@code Object}</td><td>{true}</td></tr>
* </table>
* @return the set of boolean literals for this type
*/
public abstract BooleanLiteralSet get
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> by
* {@link #isEquivalentTo}. It may or may not be the same object. This method
* may modify the internal state of {@code this}, as long as it does
* so in a way that preserves Object equality.
*
* For efficiency, we should only resolve a type once per compilation job.
* For incremental compilations, one compilation job may need the
* artifacts from a previous generation, so we will eventually need
* a generational flag instead of a boolean one.
*/
public final JSType resolve(ErrorReporter t, StaticScope<JSType> scope) {
if (resolved) {
// TODO(nicksantos): Check to see if resolve() looped back on itself.
// Preconditions.checkNotNull(resolveResult);
if (resolveResult == null) {
return registry.getNativeType(JSTypeNative.UNKNOWN_TYPE);
}
return resolveResult;
}
resolved = true;
resolveResult = resolveInternal(t, scope);
resolveResult.setResolvedTypeInternal(resolveResult);
return resolveResult;
}
/**
* @see #resolve
*/
abstract JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope);
void setResolvedTypeInternal(JSType type) {
resolveResult = type;
resolved = true;
}
/** Whether the type has been resolved. */
public final boolean isResolved() {
return resolved;
}
/** Clears the resolved field. */
public final void clearResolved() {
resolved = false;
resolveResult = null;
}
/**
* A null-safe resolve.
* @see #resolve
*/
static final JSType safeResolve(
JSType type, ErrorReporter t, StaticScope<JSType> scope) {
return type == null ? null : type.resolve(t, scope);
}
/**
* Certain types have constraints on them at resolution-time.
* For example, a type in an {@code @extends} annotation must be an
* object. Clients should inject a validator that emits a warning
* if the type does not validate, and return false.
*/
public boolean setValidator(Predicate<JSType> validator) {
return validator.apply(this);
}
public static class TypePair {
public final JSType typeA;
public final JSType typeB;
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> instanceof ArrowType)) {
return false;
}
ArrowType that = (ArrowType) object;
if (!returnType.isEquivalentTo(that.returnType)) {
return false;
}
return hasEqualParameters(that);
}
@Override
public int hashCode() {
int hashCode = 0;
if (returnType != null) {
hashCode += returnType.hashCode();
}
if (returnTypeInferred) {
hashCode += 1;
}
if (parameters != null) {
Node param = parameters.getFirstChild();
while (param != null) {
JSType paramType = param.getJSType();
if (paramType != null) {
hashCode += paramType.hashCode();
}
param = param.getNext();
}
}
return hashCode;
}
@Override
public JSType getLeastSupertype(JSType that) {
throw new UnsupportedOperationException();
}
@Override
public JSType getGreatestSubtype(JSType that) {
throw new UnsupportedOperationException();
}
@Override
public TernaryValue testForEquality(JSType that) {
throw new UnsupportedOperationException();
}
@Override
public <T> T visit(Visitor<T> visitor) {
throw new UnsupportedOperationException();
}
@Override
public BooleanLiteralSet getPossibleToBooleanOutcomes() {
return BooleanLiteralSet.TRUE;
}
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
returnType = safeResolve(returnType, t, scope);
if (parameters != null) {
for (Node paramNode = parameters.getFirstChild();
paramNode != null; paramNode = paramNode.getNext()) {
paramNode.setJSType(paramNode.getJSType().resolve(t, scope));
}
}
return this;
}
boolean hasUnknownParamsOrReturn() {
if (parameters != null) {
for (Node paramNode = parameters.getFirstChild();
paramNode != null; paramNode = paramNode.getNext()) {
JSType type = paramNode.getJSType();
if (type == null || type.isUnknownType()) {
return true;
}
}
}
return returnType == null || returnType.isUnknownType();
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
}
/**
* A piece of information in a marker containing a position with a string.
*/
public static class StringPosition extends SourcePosition<String> {
}
/**
* A piece of information in a marker containing a position with a type.
*/
public static class TypePosition extends SourcePosition<Node> {
public boolean hasBrackets = false;
}
/**
* Defines a class for containing the parsing information
* for this JSDocInfo. For each annotation found in the
* JsDoc, a marker will be created indicating the annotation
* itself, the name of the annotation (if any; for example,
* a @param has a name, but a @return does not), the
* textual description found on that annotation and, if applicable,
* the type declaration. All this information is only collected
* if documentation collection is turned on.
*/
public static final class Marker {
public StringPosition annotation = null;
public StringPosition name = null;
public StringPosition description = null;
public TypePosition type = null;
}
private LazilyInitializedInfo info = null;
private LazilyInitializedDocumentation documentation = null;
/** The source file containing the JSDoc. */
private String sourceName = null;
private Visibility visibility = null;
/**
* The {@link #isConstant()}, {@link #isConstructor()}, {@link #isInterface},
* {@link #isHidden()} and {@link #shouldPreserveTry()} flags as well as
* whether the {@link #type} field stores a value for {@link #getType()},
* {@link #getReturnType()} or {@link #getEnumParameterType()}.
*
* @see #setFlag(boolean, int)
* @see #getFlag(int)
* @see #setType(JSTypeExpression, int)
* @see #getType(int)
*/
private int bitset = 0x00;
/**
* The type for {@link #getType()}, {@link #getReturnType()} or
* {@link #getEnumParameterType()}. The knowledge of which one is recorded is
* stored in the {@link #bitset} field.
*
* @see #setType(JSTypeExpression, int)
* @see #getType(int)
*/
private JSTypeExpression type = null;
/**
* The type for {@link #
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>4000; // @nosideeffects
private static final int MASK_EXTERNS = 0x00008000; // @externs
private static final int MASK_JAVADISPATCH = 0x00010000; // @javadispath
private static final int MASK_NOCOMPILE = 0x00020000; // @nocompile
// 3 bit type field stored in the top 3 bits of the most significant
// nibble.
private static final int MASK_TYPEFIELD = 0xE0000000; // 1110...
private static final int TYPEFIELD_TYPE = 0x20000000; // 0010...
private static final int TYPEFIELD_RETURN = 0x40000000; // 0100...
private static final int TYPEFIELD_ENUM = 0x60000000; // 0110...
private static final int TYPEFIELD_TYPEDEF = 0x80000000; // 1000...
/**
* Creates a {@link JSDocInfo} object. This object should be created using
* a {@link JSDocInfoBuilder}.
*/
JSDocInfo(boolean includeDocumentation) {
this.includeDocumentation = includeDocumentation;
}
// Visible for testing.
public JSDocInfo() {}
void setConstant(boolean value) {
setFlag(value, MASK_CONSTANT);
}
void setConstructor(boolean value) {
setFlag(value, MASK_CONSTRUCTOR);
}
void setDefine(boolean value) {
setFlag(value, MASK_DEFINE);
}
void setHidden(boolean value) {
setFlag(value, MASK_HIDDEN);
}
void setNoCheck(boolean value) {
setFlag(value, MASK_NOCHECK);
}
void setShouldPreserveTry(boolean value) {
setFlag(value, MASK_PRESERVETRY);
}
void setOverride(boolean value) {
setFlag(value, MASK_OVERRIDE);
}
void setNoAlias(boolean value) {
setFlag(value, MASK_NO
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>ALIAS);
}
// Visible for testing.
public void setDeprecated(boolean value) {
setFlag(value, MASK_DEPRECATED);
}
void setInterface(boolean value) {
setFlag(value, MASK_INTERFACE);
}
void setExport(boolean value) {
setFlag(value, MASK_EXPORT);
}
void setNoShadow(boolean value) {
setFlag(value, MASK_NOSHADOW);
}
void setImplicitCast(boolean value) {
setFlag(value, MASK_IMPLICITCAST);
}
void setNoSideEffects(boolean value) {
setFlag(value, MASK_NOSIDEEFFECTS);
}
void setExterns(boolean value) {
setFlag(value, MASK_EXTERNS);
}
void setJavaDispatch(boolean value) {
setFlag(value, MASK_JAVADISPATCH);
}
void setNoCompile(boolean value) {
setFlag(value, MASK_NOCOMPILE);
}
private void setFlag(boolean value, int mask) {
if (value) {
bitset |= mask;
} else {
bitset &= ~mask;
}
}
/**
* Returns whether the {@code @const} annotation is present on this
* {@link JSDocInfo}.
*/
public boolean isConstant() {
return getFlag(MASK_CONSTANT) || isDefine();
}
/**
* Returns whether the {@code @constructor} annotation is present on this
* {@link JSDocInfo}.
*/
public boolean isConstructor() {
return getFlag(MASK_CONSTRUCTOR);
}
/**
* Returns whether the {@code @define} annotation is present on this
* {@link JSDocInfo}. If this annotation is present, then the
* {@link #getType()} method will retrieve the define type.
*/
public boolean isDefine() {
return getFlag(MASK_DEFINE);
}
/**
* Returns whether the {@code @hidden} annotation is present on this
* {@link JSDocInfo}.
*/
public boolean isHidden() {
return getFlag(MASK_HIDDEN);
}
/**
* Returns whether the {@code @nocheck} annotation is present on this
* {@link JSDocInfo}.
*/
public boolean isNoTypeCheck()
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>.authors == null) {
documentation.authors = Lists.newArrayList();
}
documentation.authors.add(author);
return true;
}
/**
* Documents the throws (i.e. adds it to the throws list).
*/
boolean documentThrows(JSTypeExpression type, String throwsDescription) {
if (!lazyInitDocumentation()) {
return true;
}
if (documentation.throwsDescriptions == null) {
documentation.throwsDescriptions =
new LinkedHashMap<JSTypeExpression, String>();
}
if (!documentation.throwsDescriptions.containsKey(type)) {
documentation.throwsDescriptions.put(type, throwsDescription);
return true;
}
return false;
}
/**
* Documents a parameter. Parameters are described using the {@code @param}
* annotation.
*
* @param parameter the parameter's name
* @param description the parameter's description
*/
boolean documentParam(String parameter, String description) {
if (!lazyInitDocumentation()) {
return true;
}
if (documentation.parameters == null) {
documentation.parameters = new LinkedHashMap<String, String>();
}
if (!documentation.parameters.containsKey(parameter)) {
documentation.parameters.put(parameter, description);
return true;
} else {
return false;
}
}
/**
* Documents the block-level comment/description.
*
* @param description the description
*/
boolean documentBlock(String description) {
if (!lazyInitDocumentation()) {
return true;
}
if (documentation.blockDescription != null) {
return false;
}
documentation.blockDescription = description;
return true;
}
/**
* Documents the fileoverview comment/description.
*
* @param description the description
*/
boolean documentFileOverview(String description) {
setFlag(true, MASK_FILEOVERVIEW);
if (!lazyInitDocumentation()) {
return true;
}
if (documentation.fileOverview != null) {
return false;
}
documentation.fileOverview = description;
return true;
}
/**
* Documents the return value. Return value is described using the
* {@code @return} annotation.
*
* @param description the return value's description
*/
boolean documentReturn(String description) {
if (!lazyInitDocumentation()) {
return true;
}
if
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
}
return info.parameters.get(parameter);
}
/**
* Returns whether the parameter is defined.
*/
public boolean hasParameter(String parameter) {
if (info == null || info.parameters == null) {
return false;
}
return info.parameters.containsKey(parameter);
}
/**
* Returns whether the parameter has an attached type.
*
* @return {@code true} if the parameter has an attached type, {@code false}
* if the parameter has no attached type or does not exist.
*/
public boolean hasParameterType(String parameter) {
return getParameterType(parameter) != null;
}
/**
* Returns the set of names of the defined parameters. The iteration order
* of the returned set is not the order in which parameters are defined.
*
* @return the set of names of the defined parameters. The returned set is
* immutable.
*/
public Set<String> getParameterNames() {
if (info == null || info.parameters == null) {
return ImmutableSet.of();
}
return ImmutableSet.copyOf(info.parameters.keySet());
}
/**
* Gets the number of parameters defined.
*/
public int getParameterCount() {
if (info == null || info.parameters == null) {
return 0;
}
return info.parameters.size();
}
void setType(JSTypeExpression type) {
setType(type, TYPEFIELD_TYPE);
}
void setReturnType(JSTypeExpression type) {
setType(type, TYPEFIELD_RETURN);
}
void setEnumParameterType(JSTypeExpression type) {
setType(type, TYPEFIELD_ENUM);
}
void setTypedefType(JSTypeExpression type) {
setType(type, TYPEFIELD_TYPEDEF);
}
private void setType(JSTypeExpression type, int mask) {
if ((bitset & MASK_TYPEFIELD) != 0) {
throw new IllegalStateException(
"API tried to add two incompatible type tags. " +
"This should have been blocked and emitted a warning.");
}
this.bitset = (bitset & MASK_FLAGS) | mask;
this.type = type;
}
/**
* Returns the list of thrown types.
*/
public List<JSTypeExpression> get
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>ThrownTypes() {
if (info == null || info.thrownTypes == null) {
return ImmutableList.of();
}
return Collections.unmodifiableList(info.thrownTypes);
}
/**
* Returns whether a type, specified using the {@code @type} annotation, is
* present on this JSDoc.
*/
public boolean hasType() {
return hasType(TYPEFIELD_TYPE);
}
/**
* Returns whether an enum parameter type, specified using the {@code @enum}
* annotation, is present on this JSDoc.
*/
public boolean hasEnumParameterType() {
return hasType(TYPEFIELD_ENUM);
}
/**
* Returns whether a typedef parameter type, specified using the
* {@code @typedef} annotation, is present on this JSDoc.
*/
public boolean hasTypedefType() {
return hasType(TYPEFIELD_TYPEDEF);
}
/**
* Returns whether this {@link JSDocInfo} contains a type for {@code @return}
* annotation.
*/
public boolean hasReturnType() {
return hasType(TYPEFIELD_RETURN);
}
private boolean hasType(int mask) {
return (bitset & MASK_TYPEFIELD) == mask;
}
/**
* Gets the type specified by the {@code @type} annotation.
*/
public JSTypeExpression getType() {
return getType(TYPEFIELD_TYPE);
}
/**
* Gets the return type specified by the {@code @return} annotation.
*/
public JSTypeExpression getReturnType() {
return getType(TYPEFIELD_RETURN);
}
/**
* Gets the enum parameter type specified by the {@code @enum} annotation.
*/
public JSTypeExpression getEnumParameterType() {
return getType(TYPEFIELD_ENUM);
}
/**
* Gets the typedef type specified by the {@code @type} annotation.
*/
public JSTypeExpression getTypedefType() {
return getType(TYPEFIELD_TYPEDEF);
}
private JSTypeExpression getType(int typefield) {
if ((MASK_TYPEFIELD & bitset) == typefield) {
return type;
} else {
return null;
}
}
/**
* Gets the type specified by the {@code @this} annotation.
*/
public JSTypeExpression getThisType() {
return thisType;
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
}
/**
* Sets the type specified by the {@code @this} annotation.
*/
void setThisType(JSTypeExpression type) {
this.thisType = type;
}
/**
* Returns whether this {@link JSDocInfo} contains a type for {@code @this}
* annotation.
*/
public boolean hasThisType() {
return thisType != null;
}
void setBaseType(JSTypeExpression type) {
lazyInitInfo();
info.baseType = type;
}
/**
* Gets the base type specified by the {@code @extends} annotation.
*/
public JSTypeExpression getBaseType() {
return (info == null) ? null : info.baseType;
}
/**
* Gets the description specified by the {@code @desc} annotation.
*/
public String getDescription() {
return (info == null) ? null : info.description;
}
void setDescription(String desc) {
lazyInitInfo();
info.description = desc;
}
/**
* Gets the meaning specified by the {@code @meaning} annotation.
*
* In localization systems, two messages with the same content but
* different "meanings" may be translated differently. By default, we
* use the name of the variable that the message is initialized to as
* the "meaning" of the message.
*
* But some code generators (like Closure Templates) inject their own
* meaning with the jsdoc {@code @meaning} annotation.
*/
public String getMeaning() {
return (info == null) ? null : info.meaning;
}
void setMeaning(String meaning) {
lazyInitInfo();
info.meaning = meaning;
}
/**
* Gets the name we're lending to in a {@code @lends} annotation.
*
* In many reflection APIs, you pass an anonymous object to a function,
* and that function mixes the anonymous object into another object.
* The {@code @lends} annotation allows the type system to track
* those property assignments.
*/
public String getLendsName() {
return (info == null) ? null : info.lendsName;
}
void setLendsName(String name) {
lazyInitInfo();
info.lendsName = name;
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> or null if none specified.
*/
public String getFileOverview() {
return documentation == null ? null : documentation.fileOverview;
}
/** Gets the name of the source file that contains this JSDoc. */
public String getSourceName() {
return sourceName;
}
/** Gets the list of all markers for the documentation in this JSDoc. */
public Collection<Marker> getMarkers() {
return documentation == null ? null : documentation.markers;
}
/** Sets the name of the source file that contains this JSDoc. */
void setSourceName(String sourceName) {
this.sourceName = sourceName;
}
/** Gets the template type name. */
public String getTemplateTypeName() {
if (info == null) {
return null;
}
return info.templateTypeName;
}
/**
* Returns a collection of all type nodes that are a part of this JSDocInfo.
* This includes @type, @this, @extends, @implements, @param, @throws,
* and @return. Any future type specific JSDoc should make sure to add the
* appropriate nodes here.
* @return collection of all type nodes
*/
public Collection<Node> getTypeNodes() {
List<Node> nodes = Lists.newArrayList();
if (type != null) {
nodes.add(type.getRoot());
}
if (thisType != null) {
nodes.add(thisType.getRoot());
}
if (info != null) {
if (info.baseType != null) {
nodes.add(info.baseType.getRoot());
}
if (info.implementedInterfaces != null) {
for (JSTypeExpression interfaceType : info.implementedInterfaces) {
nodes.add(interfaceType.getRoot());
}
}
if (info.parameters != null) {
for (JSTypeExpression parameterType : info.parameters.values()) {
if (parameterType != null) {
nodes.add(parameterType.getRoot());
}
}
}
if (info.thrownTypes != null) {
for (JSTypeExpression thrownType : info.thrownTypes) {
if (thrownType != null) {
nodes.add(thrownType.getRoot());
}
}
}
}
return nodes;
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> RecordTypeBuilder(registry);
}
protected JSType createNullableType(JSType type) {
return registry.createNullableType(type);
}
protected JSType createOptionalType(JSType type) {
return registry.createOptionalType(type);
}
/**
* Asserts that a Node representing a type expression resolves to the
* correct {@code JSType}.
*/
protected void assertTypeEquals(JSType expected, Node actual) {
assertTypeEquals(expected, new JSTypeExpression(actual, ""));
}
/**
* Asserts that a a type expression resolves to the correct {@code JSType}.
*/
protected void assertTypeEquals(JSType expected, JSTypeExpression actual) {
assertEquals(expected, resolve(actual));
}
/**
* Resolves a type expression, expecting the given warnings.
*/
protected JSType resolve(JSTypeExpression n, String... warnings) {
errorReporter.setWarnings(warnings);
return n.evaluate(null, registry);
}
/**
* A definition of all extern types. This should be kept in sync with
* javascript/externs/es3.js. This is used to check that the builtin types
* declared in {@link JSTypeRegistry} have the same type as that in the
* externs. It can also be used for any tests that want to use builtin types
* in their externs.
*/
public static final String ALL_NATIVE_EXTERN_TYPES =
"/**\n"
+ " * @constructor\n"
+ " * @param {*} opt_value\n"
+ " */\n"
+ "function Object(opt_value) {}\n"
+ "\n"
+ "/**\n"
+ " * @constructor\n"
+ " * @extends {Object}\n"
+ " * @param {*} var_args\n"
+ " */\n"
+ "\n"
+ "function Function(var_args) {}\n"
+ "/**\n"
+ " * @constructor\n"
+ " * @extends {Object}\n"
+ " * @param {*} var_args\n"
+ " * @return {!Array}\n"
+ " */\n"
+ "function Array(var_args) {}\n"
+ "\n"
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>_INITIALIZER =
DiagnosticType.warning(
"JSC_IFACE_INITIALIZER_NOT_IFACE",
"Interface {0} must be initialized at declaration");
static final DiagnosticType CONSTRUCTOR_EXPECTED =
DiagnosticType.warning(
"JSC_REFLECT_CONSTRUCTOR_EXPECTED",
"Constructor expected as first argument");
static final DiagnosticType UNKNOWN_LENDS =
DiagnosticType.warning(
"JSC_UNKNOWN_LENDS",
"Variable {0} not declared before @lends annotation.");
static final DiagnosticType LENDS_ON_NON_OBJECT =
DiagnosticType.warning(
"JSC_LENDS_ON_NON_OBJECT",
"May only lend properties to object types. {0} has type {1}.");
private final AbstractCompiler compiler;
private final ErrorReporter typeParsingErrorReporter;
private final TypeValidator validator;
private final CodingConvention codingConvention;
private final JSTypeRegistry typeRegistry;
private final List<ObjectType> delegateProxyPrototypes = Lists.newArrayList();
/**
* Defer attachment of types to nodes until all type names
* have been resolved. Then, we can resolve the type and attach it.
*/
private class DeferredSetType {
final Node node;
final JSType type;
DeferredSetType(Node node, JSType type) {
Preconditions.checkNotNull(node);
Preconditions.checkNotNull(type);
this.node = node;
this.type = type;
// Other parts of this pass may read off the node.
// (like when we set the LHS of an assign with a typed RHS function.)
node.setJSType(type);
}
void resolve(Scope scope) {
node.setJSType(type.resolve(typeParsingErrorReporter, scope));
}
}
TypedScopeCreator(AbstractCompiler compiler) {
this(compiler, compiler.getCodingConvention());
}
TypedScopeCreator(AbstractCompiler compiler,
CodingConvention codingConvention) {
this.compiler = compiler;
this.validator = compiler.getTypeValidator();
this.codingConvention = codingConvention;
this.typeRegistry = compiler.getTypeRegistry();
this.typeParsingErrorReporter = typeRegistry.getErrorReporter();
}
/**
* Creates a scope with all types declared. Declares newly discovered types
* and type properties in
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> the type registry.
*/
public Scope createScope(Node root, Scope parent) {
// Constructing the global scope is very different than constructing
// inner scopes, because only global scopes can contain named classes that
// show up in the type registry.
Scope newScope = null;
AbstractScopeBuilder scopeBuilder = null;
if (parent == null) {
// Find all the classes in the global scope.
newScope = createInitialScope(root);
GlobalScopeBuilder globalScopeBuilder = new GlobalScopeBuilder(newScope);
scopeBuilder = globalScopeBuilder;
NodeTraversal.traverse(compiler, root, scopeBuilder);
} else {
newScope = new Scope(parent, root);
LocalScopeBuilder localScopeBuilder = new LocalScopeBuilder(newScope);
scopeBuilder = localScopeBuilder;
localScopeBuilder.build();
}
scopeBuilder.resolveStubDeclarations();
scopeBuilder.resolveTypes();
// Gather the properties in each function that we found in the
// global scope, if that function has a @this type that we can
// build properties on.
for (Node functionNode : scopeBuilder.nonExternFunctions) {
JSType type = functionNode.getJSType();
if (type != null && type instanceof FunctionType) {
FunctionType fnType = (FunctionType) type;
ObjectType fnThisType = fnType.getTypeOfThis();
if (!fnThisType.isUnknownType()) {
NodeTraversal.traverse(compiler, functionNode.getLastChild(),
scopeBuilder.new CollectProperties(fnThisType));
}
}
}
if (parent == null) {
codingConvention.defineDelegateProxyPrototypeProperties(
typeRegistry, newScope, delegateProxyPrototypes);
}
return newScope;
}
/**
* Create the outermost scope. This scope contains native binding such as
* {@code Object}, {@code Date}, etc.
*/
@VisibleForTesting
Scope createInitialScope(Node root) {
NodeTraversal.traverse(
compiler, root, new DiscoverEnumsAndTypedefs(typeRegistry));
Scope s = new Scope(root, compiler);
declareNativeFunctionType(s, ARRAY_FUNCTION_TYPE);
declareNativeFunctionType(s, BOOLEAN_OBJECT_FUNCTION_TYPE);
declareNativeFunctionType(s, DATE_FUNCTION_TYPE);
declareNativeFunctionType(s, ERROR_FUNCTION
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> builidng.
*/
final Scope scope;
private final List<DeferredSetType> deferredSetTypes =
Lists.newArrayList();
/**
* Functions that we found in the global scope and not in externs.
*/
private final List<Node> nonExternFunctions = Lists.newArrayList();
/**
* Type-less stubs.
*
* If at the end of traversal, we still don't have types for these
* stubs, then we should declare UNKNOWN types.
*/
private final List<StubDeclaration> stubDeclarations =
Lists.newArrayList();
/**
* The current source file that we're in.
*/
private String sourceName = null;
private AbstractScopeBuilder(Scope scope) {
this.scope = scope;
}
void setDeferredType(Node node, JSType type) {
deferredSetTypes.add(new DeferredSetType(node, type));
}
void resolveTypes() {
// Resolve types and attach them to nodes.
for (DeferredSetType deferred : deferredSetTypes) {
deferred.resolve(scope);
}
// Resolve types and attach them to scope slots.
Iterator<Var> vars = scope.getVars();
while (vars.hasNext()) {
vars.next().resolveType(typeParsingErrorReporter);
}
// Tell the type registry that any remaining types
// are unknown.
typeRegistry.resolveTypesInScope(scope);
}
@Override
public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n,
Node parent) {
if (n.getType() == Token.FUNCTION ||
n.getType() == Token.SCRIPT) {
sourceName = (String) n.getProp(Node.SOURCENAME_PROP);
}
// We do want to traverse the name of a named function, but we don't
// want to traverse the arguments or body.
boolean descend = parent == null || parent.getType() != Token.FUNCTION ||
n == parent.getFirstChild() || parent == scope.getRootNode();
if (descend) {
// Handle hoisted functions on pre-order traversal, so that they
// get hit before other things in the scope.
if (NodeUtil.isStatementParent(n)) {
for (Node child = n.getFirstChild();
child != null;
child = child.getNext()) {
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
n.setJSType(getNativeType(BOOLEAN_TYPE));
break;
case Token.REGEXP:
n.setJSType(getNativeType(REGEXP_TYPE));
break;
case Token.REF_SPECIAL:
n.setJSType(getNativeType(UNKNOWN_TYPE));
break;
case Token.OBJECTLIT:
defineObjectLiteral(t, n);
break;
// NOTE(nicksantos): If we ever support Array tuples,
// we will need to put ARRAYLIT here as well.
}
}
private void defineObjectLiteral(NodeTraversal t, Node objectLit) {
// Handle the @lends annotation.
JSType type = null;
JSDocInfo info = objectLit.getJSDocInfo();
if (info != null &&
info.getLendsName() != null) {
String lendsName = info.getLendsName();
Var lendsVar = scope.getVar(lendsName);
if (lendsVar == null) {
compiler.report(
JSError.make(sourceName, objectLit, UNKNOWN_LENDS, lendsName));
} else {
type = lendsVar.getType();
if (type == null) {
type = typeRegistry.getNativeType(UNKNOWN_TYPE);
}
if (!type.isSubtype(typeRegistry.getNativeType(OBJECT_TYPE))) {
compiler.report(
JSError.make(sourceName, objectLit, LENDS_ON_NON_OBJECT,
lendsName, type.toString()));
type = null;
} else {
objectLit.setJSType(type);
}
}
}
info = getBestJSDocInfo(objectLit);
Node lValue = getBestLValue(objectLit);
String lValueName = getBestLValueName(lValue);
boolean createdEnumType = false;
if (info != null && info.hasEnumParameterType()) {
type = createEnumTypeFromNodes(objectLit, lValueName, info, lValue);
createdEnumType = true;
}
if (type == null) {
type = typeRegistry.createAnonymousObjectType();
}
setDeferredType(objectLit, type);
// If this is an enum, the properties were already taken care of above.
if
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> (!createdEnumType) {
processObjectLitProperties(
t, objectLit, ObjectType.cast(objectLit.getJSType()));
}
}
/**
* Process an object literal and all the types on it.
* @param objLit The OBJECTLIT node.
* @param objLitType The type of the OBJECTLIT node. This might be a named
* type, because of the lends annotation.
*/
void processObjectLitProperties(
NodeTraversal t, Node objLit, ObjectType objLitType) {
for (Node keyNode = objLit.getFirstChild(); keyNode != null;
keyNode = keyNode.getNext()) {
Node value = keyNode.getFirstChild();
String memberName = NodeUtil.getObjectLitKeyName(keyNode);
JSDocInfo info = keyNode.getJSDocInfo();
JSType valueType = getDeclaredType(
t.getSourceName(), info, keyNode, value);
JSType keyType = NodeUtil.getObjectLitKeyTypeFromValueType(
keyNode, valueType);
if (keyType != null) {
// Try to declare this property in the current scope if it
// has an authoritative name.
String qualifiedName = getBestLValueName(keyNode);
if (qualifiedName != null) {
defineSlot(keyNode, objLit, qualifiedName, keyType, false);
} else {
setDeferredType(keyNode, keyType);
}
if (objLitType != null) {
// Declare this property on its object literal.
boolean isExtern = t.getInput() != null && t.getInput().isExtern();
objLitType.defineDeclaredProperty(
memberName, keyType, isExtern, keyNode);
}
}
}
}
/**
* Returns the type specified in a JSDoc annotation near a GETPROP or NAME.
*
* Extracts type information from either the {@code @type} tag or from
* the {@code @return} and {@code @param} tags.
*/
private JSType getDeclaredTypeInAnnotation(String sourceName,
Node node, JSDocInfo info) {
JSType jsType = null;
Node objNode =
node.getType() == Token.GETPROP ? node.getFirstChild() :
NodeUtil.isObjectLitKey(node, node
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>.getParent()) ? node.getParent() :
null;
if (info != null) {
if (info.hasType()) {
jsType = info.getType().evaluate(scope, typeRegistry);
} else if (FunctionTypeBuilder.isFunctionTypeDeclaration(info)) {
String fnName = node.getQualifiedName();
jsType = createFunctionTypeFromNodes(
null, fnName, info, node);
}
}
return jsType;
}
/**
* Asserts that it's ok to define this node's name.
* The node should have a source name and be of the specified type.
*/
void assertDefinitionNode(Node n, int type) {
Preconditions.checkState(sourceName != null);
Preconditions.checkState(n.getType() == type);
}
/**
* Defines a catch parameter.
*/
void defineCatch(Node n, Node parent) {
assertDefinitionNode(n, Token.CATCH);
Node catchName = n.getFirstChild();
defineSlot(catchName, n, null);
}
/**
* Defines a VAR initialization.
*/
void defineVar(Node n, Node parent) {
assertDefinitionNode(n, Token.VAR);
JSDocInfo info = n.getJSDocInfo();
if (n.hasMoreThanOneChild()) {
if (info != null) {
// multiple children
compiler.report(JSError.make(sourceName, n, MULTIPLE_VAR_DEF));
}
for (Node name : n.children()) {
defineName(name, n, parent, name.getJSDocInfo());
}
} else {
Node name = n.getFirstChild();
defineName(name, n, parent,
(info != null) ? info : name.getJSDocInfo());
}
}
/**
* Defines a function literal.
*/
void defineFunctionLiteral(Node n, Node parent) {
assertDefinitionNode(n, Token.FUNCTION);
// Determine the name and JSDocInfo and lvalue for the function.
// Any of these may be null.
Node lValue = getBestLValue(n);
JSDocInfo info = getBestJSDocInfo(n);
String functionName = getBestLValueName(lValue);
FunctionType functionType =
createFunctionType
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>FromNodes(n, functionName, info, lValue);
// Assigning the function type to the function node
setDeferredType(n, functionType);
// Declare this symbol in the current scope iff it's a function
// declaration. Otherwise, the declaration will happen in other
// code paths.
if (NodeUtil.isFunctionDeclaration(n)) {
defineSlot(n.getFirstChild(), n, functionType);
}
}
/**
* Defines a variable based on the {@link Token#NAME} node passed.
* @param name The {@link Token#NAME} node.
* @param var The parent of the {@code name} node, which must be a
* {@link Token#VAR} node.
* @param parent {@code var}'s parent.
* @param info the {@link JSDocInfo} information relating to this
* {@code name} node.
*/
private void defineName(Node name, Node var, Node parent, JSDocInfo info) {
Node value = name.getFirstChild();
// variable's type
JSType type = getDeclaredType(sourceName, info, name, value);
if (type == null) {
// The variable's type will be inferred.
CompilerInput input = compiler.getInput(sourceName);
Preconditions.checkNotNull(input, sourceName);
type = input.isExtern() ?
getNativeType(UNKNOWN_TYPE) : null;
}
defineSlot(name, var, type);
}
/**
* If a variable is assigned a function literal in the global scope,
* make that a declared type (even if there's no doc info).
* There's only one exception to this rule:
* if the return type is inferred, and we're in a local
* scope, we should assume the whole function is inferred.
*/
private boolean shouldUseFunctionLiteralType(
FunctionType type, JSDocInfo info, Node lValue) {
if (info != null) {
return true;
}
if (lValue != null &&
NodeUtil.isObjectLitKey(lValue, lValue.getParent())) {
return false;
}
return scope.isGlobal() || !type.isReturnTypeInferred();
}
/**
* Creates a new function type, based on the given nodes.
*
* This handles
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>;
if (info != null && info.hasType()) {
JSType type = info.getType().evaluate(scope, typeRegistry);
// Known to be not null since we have the FUNCTION token there.
type = type.restrictByNotNullOrUndefined();
if (type.isFunctionType()) {
functionType = (FunctionType) type;
functionType.setJSDocInfo(info);
}
}
if (functionType == null) {
// Find the type of any overridden function.
FunctionType overriddenPropType = null;
if (lvalueNode != null &&
lvalueNode.getType() == Token.GETPROP &&
lvalueNode.isQualifiedName()) {
Var var = scope.getVar(
lvalueNode.getFirstChild().getQualifiedName());
if (var != null) {
ObjectType ownerType = ObjectType.cast(var.getType());
if (ownerType != null) {
String propName = lvalueNode.getLastChild().getString();
overriddenPropType =
findOverriddenFunction(ownerType, propName);
}
}
}
FunctionTypeBuilder builder =
new FunctionTypeBuilder(name, compiler, errorRoot, sourceName,
scope)
.setSourceNode(fnRoot)
.inferFromOverriddenFunction(overriddenPropType, parametersNode)
.inferTemplateTypeName(info)
.inferReturnType(info)
.inferInheritance(info);
// Infer the context type.
boolean searchedForThisType = false;
if (lvalueNode != null &&
lvalueNode.getType() == Token.GETPROP) {
Node objNode = lvalueNode.getFirstChild();
if (objNode.getType() == Token.GETPROP &&
objNode.getLastChild().getString().equals("prototype")) {
builder.inferThisType(info, objNode.getFirstChild());
searchedForThisType = true;
} else if (objNode.getType() == Token.THIS) {
builder.inferThisType(info, objNode.getJSType());
searchedForThisType = true;
}
}
if (!searchedForThisType) {
builder.inferThisType(info, (Node) null);
}
functionType = builder
.inferParameterTypes(parametersNode, info)
.inferReturnStatementsAsLastResort(fnBlock)
.buildAnd
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>Registry.createEnumType(name, elementsType);
if (rValue != null && rValue.getType() == Token.OBJECTLIT) {
// collect enum elements
Node key = rValue.getFirstChild();
while (key != null) {
String keyName = NodeUtil.getStringValue(key);
if (keyName == null) {
// GET and SET don't have a String value;
compiler.report(
JSError.make(sourceName, key, ENUM_NOT_CONSTANT, keyName));
} else if (enumType.hasOwnProperty(keyName)) {
compiler.report(JSError.make(sourceName, key, ENUM_DUP, keyName));
} else if (!codingConvention.isValidEnumKey(keyName)) {
compiler.report(
JSError.make(sourceName, key, ENUM_NOT_CONSTANT, keyName));
} else {
enumType.defineElement(keyName, key);
}
key = key.getNext();
}
}
}
if (name != null && scope.isGlobal()) {
typeRegistry.declareType(name, enumType.getElementsType());
}
return enumType;
}
/**
* Defines a typed variable. The defining node will be annotated with the
* variable's type or {@code null} if its type is inferred.
* @param name the defining node. It must be a {@link Token#NAME}.
* @param parent the {@code name}'s parent.
* @param type the variable's type. It may be {@code null}, in which case
* the variable's type will be inferred.
*/
private void defineSlot(Node name, Node parent, JSType type) {
defineSlot(name, parent, type, type == null);
}
/**
* Defines a typed variable. The defining node will be annotated with the
* variable's type of {@link JSTypeNative#UNKNOWN_TYPE} if its type is
* inferred.
*
* Slots may be any variable or any qualified name in the global scope.
*
* @param n the defining NAME or GETPROP node.
* @param parent the {@code n}'s parent.
* @param type the variable's type. It may be {@code null} if
* {@code inferred} is {@code true}.
*/
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> void defineSlot(Node n, Node parent, JSType type, boolean inferred) {
Preconditions.checkArgument(inferred || type != null);
// Only allow declarations of NAMEs and qualfied names.
// Object literal keys will have to compute their names themselves.
if (n.getType() == Token.NAME) {
Preconditions.checkArgument(
parent.getType() == Token.FUNCTION ||
parent.getType() == Token.VAR ||
parent.getType() == Token.LP ||
parent.getType() == Token.CATCH);
} else {
Preconditions.checkArgument(
n.getType() == Token.GETPROP &&
(parent.getType() == Token.ASSIGN ||
parent.getType() == Token.EXPR_RESULT));
}
defineSlot(n, parent, n.getQualifiedName(), type, inferred);
}
/**
* Defines a symbol in the current scope.
*
* @param n the defining NAME or GETPROP or object literal key node.
* @param parent the {@code n}'s parent.
* @param variableName The name that this should be known by.
* @param type the variable's type. It may be {@code null} if
* {@code inferred} is {@code true}.
* @param inferred Whether the type is inferred or declared.
*/
void defineSlot(Node n, Node parent, String variableName,
JSType type, boolean inferred) {
Preconditions.checkArgument(!variableName.isEmpty());
boolean isGlobalVar = n.getType() == Token.NAME && scope.isGlobal();
boolean shouldDeclareOnGlobalThis =
isGlobalVar &&
(parent.getType() == Token.VAR ||
parent.getType() == Token.FUNCTION);
// If n is a property, then we should really declare it in the
// scope where the root object appears. This helps out people
// who declare "global" names in an anonymous namespace.
Scope scopeToDeclareIn = scope;
if (n.getType() == Token.GETPROP && !scope.isGlobal() &&
isQnameRootedInGlobalScope(n)) {
Scope globalScope = scope.getGlobalScope();
// don't try to declare in the global scope if there's
// already a symbol there with this name.
if (!globalScope.isDeclared(variableName, false)) {
scopeToDeclareIn
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> = scope.getGlobalScope();
}
}
// declared in closest scope?
if (scopeToDeclareIn.isDeclared(variableName, false)) {
Var oldVar = scopeToDeclareIn.getVar(variableName);
validator.expectUndeclaredVariable(
sourceName, n, parent, oldVar, variableName, type);
} else {
if (!inferred) {
setDeferredType(n, type);
}
CompilerInput input = compiler.getInput(sourceName);
boolean isExtern = input.isExtern();
Var newVar =
scopeToDeclareIn.declare(variableName, n, type, input, inferred);
if (shouldDeclareOnGlobalThis) {
ObjectType globalThis =
typeRegistry.getNativeObjectType(GLOBAL_THIS);
if (inferred) {
globalThis.defineInferredProperty(variableName,
type == null ?
getNativeType(JSTypeNative.NO_TYPE) :
type,
isExtern, n);
} else {
globalThis.defineDeclaredProperty(variableName, type, isExtern, n);
}
}
if (type instanceof EnumType) {
Node initialValue = newVar.getInitialValue();
boolean isValidValue = initialValue != null &&
(initialValue.getType() == Token.OBJECTLIT ||
initialValue.isQualifiedName());
if (!isValidValue) {
compiler.report(JSError.make(sourceName, n, ENUM_INITIALIZER));
}
}
// We need to do some additional work for constructors and interfaces.
if (type instanceof FunctionType &&
// We don't want to look at empty function types.
!type.isEmptyType()) {
FunctionType fnType = (FunctionType) type;
if ((fnType.isConstructor() || fnType.isInterface()) &&
!fnType.equals(getNativeType(U2U_CONSTRUCTOR_TYPE))) {
// Declare var.prototype in the scope chain.
FunctionType superClassCtor = fnType.getSuperClassConstructor();
scopeToDeclareIn.declare(variableName + ".prototype", n,
fnType.getPrototype(), input,
/* declared iff there's an explicit supertype */
superClassCtor == null ||
superClassCtor.getInstanceType().equals(
getNativeType(OBJECT_TYPE)));
// Make sure the variable is initialized
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> to something if
// it constructs itself.
if (newVar.getInitialValue() == null &&
!isExtern &&
// We want to make sure that when we declare a new instance
// type (with @constructor) that there's actually a ctor for it.
// This doesn't apply to structural constructors
// (like function(new:Array). Checking the constructed
// type against the variable name is a sufficient check for
// this.
variableName.equals(
fnType.getInstanceType().getReferenceName())) {
compiler.report(
JSError.make(sourceName, n,
fnType.isConstructor() ?
CTOR_INITIALIZER : IFACE_INITIALIZER,
variableName));
}
}
}
}
if (isGlobalVar && "Window".equals(variableName)
&& type instanceof FunctionType
&& type.isConstructor()) {
FunctionType globalThisCtor =
typeRegistry.getNativeObjectType(GLOBAL_THIS).getConstructor();
globalThisCtor.getInstanceType().clearCachedValues();
globalThisCtor.getPrototype().clearCachedValues();
globalThisCtor
.setPrototypeBasedOn(((FunctionType) type).getInstanceType());
}
}
/**
* Check if the given node is a property of a name in the global scope.
*/
private boolean isQnameRootedInGlobalScope(Node n) {
Node root = NodeUtil.getRootOfQualifiedName(n);
if (root.getType() == Token.NAME) {
Var var = scope.getVar(root.getString());
if (var != null) {
return var.isGlobal();
}
}
return false;
}
/**
* Look for a type declaration on a property assignment
* (in an ASSIGN or an object literal key).
*
* @param info The doc info for this property.
* @param lValue The l-value node.
* @param rValue The node that {@code n} is being initialized to,
* or {@code null} if this is a stub declaration.
*/
private JSType getDeclaredType(String sourceName, JSDocInfo info,
Node lValue, @Nullable Node rValue) {
if (info != null && info.hasType()) {
return getDeclaredTypeInAnnotation(sourceName, lValue, info);
} else if (r
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
if (relationship != null) {
ObjectType superClass = ObjectType.cast(
typeRegistry.getType(relationship.superclassName));
ObjectType subClass = ObjectType.cast(
typeRegistry.getType(relationship.subclassName));
if (superClass != null && subClass != null) {
FunctionType superCtor = superClass.getConstructor();
FunctionType subCtor = subClass.getConstructor();
if (relationship.type == SubclassType.INHERITS) {
validator.expectSuperType(t, n, superClass, subClass);
}
if (superCtor != null && subCtor != null) {
codingConvention.applySubclassRelationship(
superCtor, subCtor, relationship.type);
}
}
}
String singletonGetterClassName =
codingConvention.getSingletonGetterClassName(n);
if (singletonGetterClassName != null) {
ObjectType objectType = ObjectType.cast(
typeRegistry.getType(singletonGetterClassName));
if (objectType != null) {
FunctionType functionType = objectType.getConstructor();
if (functionType != null) {
FunctionType getterType =
typeRegistry.createFunctionType(objectType);
codingConvention.applySingletonGetter(functionType, getterType,
objectType);
}
}
}
DelegateRelationship delegateRelationship =
codingConvention.getDelegateRelationship(n);
if (delegateRelationship != null) {
applyDelegateRelationship(delegateRelationship);
}
ObjectLiteralCast objectLiteralCast =
codingConvention.getObjectLiteralCast(t, n);
if (objectLiteralCast != null) {
ObjectType type = ObjectType.cast(
typeRegistry.getType(objectLiteralCast.typeName));
if (type != null && type.getConstructor() != null) {
setDeferredType(objectLiteralCast.objectNode, type);
} else {
compiler.report(JSError.make(t.getSourceName(), n,
CONSTRUCTOR_EXPECTED));
}
}
}
/**
* Apply special properties that only apply to delegates.
*/
private void applyDelegateRelationship(
DelegateRelationship delegateRelationship) {
ObjectType delegatorObject = ObjectType.cast(
typeRegistry.getType(delegateRelationship.delegator));
ObjectType delegateBaseObject = ObjectType.cast(
typeRegistry.getType(delegateRelationship.delegateBase));
ObjectType delegateSuperObject = ObjectType.cast(
typeRegistry.getType(codingConvention
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> n.getParent();
String qName = n.getQualifiedName();
String propName = n.getLastChild().getString();
String ownerName = stub.ownerName;
boolean isExtern = stub.isExtern;
if (scope.isDeclared(qName, false)) {
continue;
}
// If we see a stub property, make sure to register this property
// in the type registry.
ObjectType ownerType = getObjectSlot(ownerName);
ObjectType unknownType = typeRegistry.getNativeObjectType(UNKNOWN_TYPE);
defineSlot(n, parent, unknownType, true);
if (ownerType != null &&
(isExtern || ownerType.isFunctionPrototypeType())) {
// If this is a stub for a prototype, just declare it
// as an unknown type. These are seen often in externs.
ownerType.defineInferredProperty(
propName, unknownType, isExtern, n);
} else {
typeRegistry.registerPropertyOnType(
propName, ownerType == null ? unknownType : ownerType);
}
}
}
/**
* Collects all declared properties in a function, and
* resolves them relative to the global scope.
*/
private final class CollectProperties
extends AbstractShallowStatementCallback {
private final ObjectType thisType;
CollectProperties(ObjectType thisType) {
this.thisType = thisType;
}
public void visit(NodeTraversal t, Node n, Node parent) {
if (n.getType() == Token.EXPR_RESULT) {
Node child = n.getFirstChild();
switch (child.getType()) {
case Token.ASSIGN:
maybeCollectMember(t, child.getFirstChild(), child,
child.getLastChild());
break;
case Token.GETPROP:
maybeCollectMember(t, child, child, null);
break;
}
}
}
private void maybeCollectMember(NodeTraversal t,
Node member, Node nodeWithJsDocInfo, @Nullable Node value) {
JSDocInfo info = nodeWithJsDocInfo.getJSDocInfo();
// Do nothing if there is no JSDoc type info, or
// if the node is not a member expression, or
// if the member expression is not of the form: this.someProperty.
if (info == null ||
member.getType() !=
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> Token.GETPROP ||
member.getFirstChild().getType() != Token.THIS) {
return;
}
member.getFirstChild().setJSType(thisType);
JSType jsType = getDeclaredType(t.getSourceName(), info, member, value);
Node name = member.getLastChild();
if (jsType != null &&
(name.getType() == Token.NAME || name.getType() == Token.STRING)) {
thisType.defineDeclaredProperty(
name.getString(),
jsType,
false /* functions with implementations are not in externs */,
member);
}
}
} // end CollectProperties
}
/**
* A stub declaration without any type information.
*/
private static final class StubDeclaration {
private final Node node;
private final boolean isExtern;
private final String ownerName;
private StubDeclaration(Node node, boolean isExtern, String ownerName) {
this.node = node;
this.isExtern = isExtern;
this.ownerName = ownerName;
}
}
/**
* A shallow traversal of the global scope to build up all classes,
* functions, and methods.
*/
private final class GlobalScopeBuilder extends AbstractScopeBuilder {
private GlobalScopeBuilder(Scope scope) {
super(scope);
}
/**
* Visit a node in the global scope, and add anything it declares to the
* global symbol table.
*
* @param t The current traversal.
* @param n The node being visited.
* @param parent The parent of n
*/
@Override public void visit(NodeTraversal t, Node n, Node parent) {
super.visit(t, n, parent);
switch (n.getType()) {
case Token.ASSIGN:
// Handle typedefs.
checkForOldStyleTypedef(t, n);
break;
case Token.VAR:
// Handle typedefs.
if (n.hasOneChild()) {
checkForOldStyleTypedef(t, n);
checkForTypedef(t, n.getFirstChild(), n.getJSDocInfo());
}
break;
}
}
@Override
void maybeDeclareQualifiedName(
NodeTraversal t, JSDocInfo info,
Node n, Node parent, Node rhsValue) {
checkForTypedef(t, n, info
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.javascript.jscomp.CheckLevel;
/**
* Class that allows to flexibly manage what to do with a reported
* warning/error.
*
* Guard has several choices:
* - return OFF - suppress the warning/error
* - return WARNING
* - return ERROR report it with high severity
* - return null. Does not know what to do with it. Lets the other guard
* decide what to do with it.
*
* Although the interface is very simple it allows you easyly customize what
* warnings you are interested in.
*
* For example there are could be several implementations:
* StrictGuard - {return ERROR}. All warnings should be treat as errors.
* SilentGuard - {if (WARNING) return OFF}. Suppress all warnings but still
* fail if js has errors.
* WhitelistGuard (if !whitelistErrors.contains(error) return ERROR) return
* error if it does not present in the whitelist.
*
* @author anatol@google.com (Anatol Pomazau)
*/
public abstract class WarningsGuard {
public static enum Priority {
MAX(1),
MIN(100),
STRICT(100),
DEFAULT(50),
SUPPRESS_BY_WHITELIST(40),
SUPPRESS_DOC(20),
FILTER_BY_PATH(1);
final int value;
Priority(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> /**
* Returns a new check level for a given error. OFF - suppress it, ERROR -
* report as error. null means that this guard does not know what to do
* with the error. Null is extremely helpful when you have a chain of
* guards. If current guard returns null, then the next in the chain should
* process it.
*
* @param error a reported error.
* @return what level given error should have.
*/
public abstract CheckLevel level(JSError error);
/**
* The priority in which warnings guards are applied. Lower means the
* guard will be applied sooner. Expressed on a scale of 1 to 100.
*/
protected int getPriority() {
return Priority.DEFAULT.value;
}
/**
* Returns whether all warnings in the given diagnostic group will be
* filtered out. Used to determine which passes to skip.
*
* @param group A group of DiagnosticTypes.
* @return Whether all warnings of these types are disabled by this guard.
*/
protected boolean disables(DiagnosticGroup group) {
return false;
}
/**
* Returns whether any of the warnings in the given diagnostic group will be
* upgraded to a warning or error.
*
* @param group A group of DiagnosticTypes.
* @return Whether any warnings of these types are enabled by this guard.
*/
protected boolean enables(DiagnosticGroup group) {
return false;
}
}
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
registry, this, baseType, isNativeObjectType()));
} else {
prototype.setImplicitPrototype(baseType);
}
}
/**
* Sets the prototype.
* @param prototype the prototype. If this value is {@code null} it will
* silently be discarded.
*/
public boolean setPrototype(FunctionPrototypeType prototype) {
if (prototype == null) {
return false;
}
// getInstanceType fails if the function is not a constructor
if (isConstructor() && prototype == getInstanceType()) {
return false;
}
boolean replacedPrototype = prototype != null;
this.prototype = prototype;
if (isConstructor() || isInterface()) {
FunctionType superClass = getSuperClassConstructor();
if (superClass != null) {
superClass.addSubType(this);
}
}
if (replacedPrototype) {
clearCachedValues();
}
return true;
}
/**
* Returns all interfaces implemented by a class or its superclass and any
* superclasses for any of those interfaces. If this is called before all
* types are resolved, it may return an incomplete set.
*/
public Iterable<ObjectType> getAllImplementedInterfaces() {
// Store them in a linked hash set, so that the compile job is
// deterministic.
Set<ObjectType> interfaces = Sets.newLinkedHashSet();
for (ObjectType type : getImplementedInterfaces()) {
addRelatedInterfaces(type, interfaces);
}
return interfaces;
}
private void addRelatedInterfaces(ObjectType instance, Set<ObjectType> set) {
FunctionType constructor = instance.getConstructor();
if (constructor != null) {
if (!constructor.isInterface()) {
return;
}
set.add(instance);
if (constructor.getSuperClassConstructor() != null) {
addRelatedInterfaces(
constructor.getSuperClassConstructor().getInstanceType(), set);
}
}
}
/** Returns interfaces implemented directly by a class or its superclass. */
public Iterable<ObjectType> getImplementedInterfaces() {
FunctionType superCtor = isConstructor() ?
getSuperClassConstructor() : null;
if (superCtor == null) {
return implementedInterfaces;
} else {
return Iterables.concat(
implementedInterfaces, superCtor.getImplementedInterfaces());
}
}
public void setImplementedInterfaces(List<ObjectType> implementedInterfaces) {
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS>
// Records this type for each implemented interface.
for (ObjectType type : implementedInterfaces) {
registry.registerTypeImplementingInterface(this, type);
}
this.implementedInterfaces = ImmutableList.copyOf(implementedInterfaces);
}
@Override
public boolean hasProperty(String name) {
return super.hasProperty(name) || "prototype".equals(name);
}
@Override
public boolean hasOwnProperty(String name) {
return super.hasOwnProperty(name) || "prototype".equals(name);
}
@Override
public JSType getPropertyType(String name) {
if ("prototype".equals(name)) {
return getPrototype();
} else {
if (!hasOwnProperty(name)) {
if ("call".equals(name)) {
// Define the "call" function lazily.
Node params = getParametersNode();
if (params == null) {
// If there's no params array, don't do any type-checking
// in this CALL function.
defineDeclaredProperty(name,
new FunctionBuilder(registry)
.withReturnType(getReturnType())
.build(),
false, source);
} else {
params = params.cloneTree();
Node thisTypeNode = Node.newString(Token.NAME, "thisType");
thisTypeNode.setJSType(
registry.createOptionalNullableType(getTypeOfThis()));
params.addChildToFront(thisTypeNode);
thisTypeNode.setOptionalArg(true);
defineDeclaredProperty(name,
new FunctionBuilder(registry)
.withParamsNode(params)
.withReturnType(getReturnType())
.build(),
false, source);
}
} else if ("apply".equals(name)) {
// Define the "apply" function lazily.
FunctionParamBuilder builder = new FunctionParamBuilder(registry);
// Ecma-262 says that apply's second argument must be an Array
// or an arguments object. We don't model the arguments object,
// so let's just be forgiving for now.
// TODO(nicksantos): Model the Arguments object.
builder.addOptionalParams(
registry.createNullableType(getTypeOfThis()),
registry.createNullableType(
registry.getNativeType(JSTypeNative.OBJECT_TYPE)));
defineDeclaredProperty(name,
new FunctionBuilder(registry)
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> .withParams(builder)
.withReturnType(getReturnType())
.build(),
false, source);
}
}
return super.getPropertyType(name);
}
}
@Override
boolean defineProperty(String name, JSType type,
boolean inferred, boolean inExterns, Node propertyNode) {
if ("prototype".equals(name)) {
ObjectType objType = type.toObjectType();
if (objType != null) {
if (objType.isEquivalentTo(prototype)) {
return true;
}
return setPrototype(
new FunctionPrototypeType(
registry, this, objType, isNativeObjectType()));
} else {
return false;
}
}
return super.defineProperty(name, type, inferred, inExterns, propertyNode);
}
@Override
public boolean isPropertyTypeInferred(String property) {
return "prototype".equals(property) ||
super.isPropertyTypeInferred(property);
}
@Override
public JSType getLeastSupertype(JSType that) {
return supAndInfHelper(that, true);
}
@Override
public JSType getGreatestSubtype(JSType that) {
return supAndInfHelper(that, false);
}
/**
* Computes the supremum or infimum of functions with other types.
* Because sup() and inf() share a lot of logic for functions, we use
* a single helper.
* @param leastSuper If true, compute the supremum of {@code this} with
* {@code that}. Otherwise compute the infimum.
* @return The least supertype or greatest subtype.
*/
private JSType supAndInfHelper(JSType that, boolean leastSuper) {
// NOTE(nicksantos): When we remove the unknown type, the function types
// form a lattice with the universal constructor at the top of the lattice,
// and the LEAST_FUNCTION_TYPE type at the bottom of the lattice.
//
// When we introduce the unknown type, it's much more difficult to make
// heads or tails of the partial ordering of types, because there's no
// clear hierarchy between the different components (parameter types and
// return types) in the ArrowType.
//
// Rather than make the situation more complicated by introducing new
// types (like unions of
Closure, 74
<FILEB>
<CHANGES>
int lhType = getNormalizedNodeType(left);
int rhType = getNormalizedNodeType(right);
<CHANGEE>
<CHANGES>
private int getNormalizedNodeType(Node n) {
int type = n.getType();
if (type == Token.NOT) {
TernaryValue value = NodeUtil.getPureBooleanValue(n);
switch (value) {
case TRUE:
return Token.TRUE;
case FALSE:
return Token.FALSE;
}
}
return type;
}
<CHANGEE>
<FILEE>
<FILEB>
*/
@SuppressWarnings("fallthrough")
private Node tryFoldComparison(Node n, Node left, Node right) {
if (!NodeUtil.isLiteralValue(left, false) ||
!NodeUtil.isLiteralValue(right, false)) {
// We only handle non-literal operands for LT and GT.
if (n.getType() != Token.GT && n.getType() != Token.LT) {
return n;
}
}
int op = n.getType();
boolean result;
// TODO(johnlenz): Use the JSType to compare nodes of different types.
boolean rightLiteral = NodeUtil.isLiteralValue(right, false);
boolean undefinedRight = ((Token.NAME == right.getType()
&& right.getString().equals("undefined"))
|| (Token.VOID == right.getType()
&& NodeUtil.isLiteralValue(right.getFirstChild(), false)));
<CHANGES>
int lhType = left.getType();
int rhType = right.getType();
<CHANGEE>
switch (lhType) {
case Token.VOID:
if (!NodeUtil.isLiteralValue(left.getFirstChild(), false)) {
return n;
} else if (!rightLiteral) {
return n;
} else {
result = compareToUndefined(right, op);
}
break;
case Token.NULL:
case Token.TRUE:
result = false;
break;
default:
return n; // don't handle that op
}
break;
default:
// assert, this should cover all consts
return n;
}
Node newNode = new Node(result ? Token.TRUE : Token.FALSE);
n.getParent().replaceChild(n, newNode);
reportCodeChange();
return newNode;
}
/**
* @return Translate NOT expressions into TRUE or FALSE when possible.
*/
<CHANGES>
<CHANGEE>
/**
* The result of the comparison as a Boolean or null if the
* result could not be determined.
*/
private Boolean compareAsNumbers(int op, Node left, Node right) {
Double leftValue = NodeUtil.getNumberValue(left);
if (leftValue == null) {
return null;
}
Double rightValue = NodeUtil.getNumberValue(right);
if (rightValue == null) {
return null;
<FILEE>
<SCANS> {
if (hasKnownTypeOfThis) {
b.append(", ");
}
Node p = call.parameters.getFirstChild();
b.append(getDebugHashCodeStringOf(p.getJSType()));
p = p.getNext();
while (p != null) {
b.append(", ");
b.append(getDebugHashCodeStringOf(p.getJSType()));
p = p.getNext();
}
}
b.append(")");
b.append(": ");
b.append(getDebugHashCodeStringOf(call.returnType));
return b.toString();
}
private String getDebugHashCodeStringOf(JSType type) {
if (type == this) {
return "me";
} else {
return type.toDebugHashCodeString();
}
}
}